{"id":26804429,"url":"https://github.com/junjiepro/doroseek","last_synced_at":"2026-05-03T22:31:21.457Z","repository":{"id":277189017,"uuid":"931531886","full_name":"junjiepro/doroseek","owner":"junjiepro","description":"Access all your OpenAI Compatible endpoints with the same base URL and API key. ","archived":false,"fork":false,"pushed_at":"2025-05-26T16:23:30.000Z","size":1382,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-20T10:01:52.407Z","etag":null,"topics":["ai","deno","denofresh","fresh","mcp","mcp-proxy-server","mcp-server","proxy","typescript","web","webapp"],"latest_commit_sha":null,"homepage":"https://github.com/junjiepro/doroseek","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/junjiepro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-12T12:43:37.000Z","updated_at":"2025-05-26T16:23:33.000Z","dependencies_parsed_at":"2025-02-12T16:41:23.633Z","dependency_job_id":"b70b3539-4ab1-4b3c-af53-c66faf4049a0","html_url":"https://github.com/junjiepro/doroseek","commit_stats":null,"previous_names":["junjiepro/denoseek","junjiepro/doroseek"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/junjiepro/doroseek","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junjiepro%2Fdoroseek","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junjiepro%2Fdoroseek/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junjiepro%2Fdoroseek/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junjiepro%2Fdoroseek/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/junjiepro","download_url":"https://codeload.github.com/junjiepro/doroseek/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junjiepro%2Fdoroseek/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32587816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"ssl_error","status_checked_at":"2026-05-03T22:09:10.534Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","deno","denofresh","fresh","mcp","mcp-proxy-server","mcp-server","proxy","typescript","web","webapp"],"created_at":"2025-03-29T22:16:41.911Z","updated_at":"2026-05-03T22:31:21.432Z","avatar_url":"https://github.com/junjiepro.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `Doroseek`\n\nA simple AI app built with Deno and Fresh.\n\n1. Access all your OpenAI Compatible endpoints with the same base URL and API\n   key. And you can share the `Doroseek` API key with others to access the\n   endpoints.\n2. As MCP server with several build-in servers.\n3. As MCP Proxy server, connect to other MCP server.\n\n![Home](/home.png)\n\n## Features\n\n- OpenAI Compatible endpoints\n  - Manage endpoints and API keys\n  - Generate `Doroseek` API keys\n  - Assign alias to models\n  - route\n    - manage route: `/{key}`\n    - OpenAI Compatible route: `/api`\n- MCP SSE Server\n  - sequentialthinking -\n    [Sequential Thinking](https://github.com/modelcontextprotocol/servers/blob/main/src/sequentialthinking)\n  - think -\n    [Think Tool MCP Server](https://github.com/PhillipRt/think-mcp-server)\n  - route\n    - `/mcp/{server}/sse?apiKey={apiKey}`\n- MCP Proxy Server\n  - route\n    - stdio:\n      `/mcp/proxy/sse?apiKey={apiKey}\u0026transport=stdio\u0026command=\u0026args=\u0026env=`\n    - sse: `/mcp/proxy/sse?apiKey={apiKey}\u0026transport=sse\u0026url=`\n  - examples\n    - [Sequential Thinking](https://github.com/modelcontextprotocol/servers/blob/main/src/sequentialthinking):\n      `/mcp/proxy/sse?apiKey={apiKey}\u0026transport=stdio\u0026command=npx\u0026args=-y @modelcontextprotocol/server-sequential-thinking\u0026env={}`\n\n---\n\n## New Core Features (MCP Based)\n\n`Doroseek` now includes several powerful MCP-based services for advanced use cases:\n\n1.  **Intranet Penetration (Tunneling):** Expose local services to the internet.\n2.  **Multi-User Communication Rooms:** Enable real-time communication between multiple clients.\n3.  **Resource Sharing:** Upload and share small files or data snippets.\n\nThese features leverage WebSockets for real-time communication and Deno KV for persistence where applicable.\n\n### 1. Intranet Penetration (Tunneling)\n\n**Concept:**\nThis feature allows you to expose services running on your private network (e.g., a local development server, a database, or any TCP/HTTP service) to the public internet through a secure tunnel established with your `Doroseek` instance.\n\n**Local Agent:**\nTo use this feature, a **local agent** (client software) must be run on the machine where the private services are accessible. `Doroseek` itself can also run in this \"Agent Mode\". For details on configuring and running `Doroseek` as a local agent, see the [Agent Mode](#agent-mode) section below. The agent is responsible for connecting to a remote `Doroseek` instance (acting as a relay), managing the tunnel, and forwarding traffic between the relay and the local services.\n\n**Registration Process (Agent to Relay):**\n\n1.  **Connection:** The local agent establishes a WebSocket connection to the `Doroseek` MCP server:\n    ```\n    wss://your-doroseek-instance/mcp/tunnel/register?apiKey=\u003cyour_api_key\u003e\n    ```\n    An API key is mandatory for registration.\n\n2.  **Registration Message:** After the WebSocket connection is established, the agent sends a `register` message. This message details the services it wishes to expose.\n    *Example `register` message from agent:*\n    ```json\n    {\n      \"type\": \"register\",\n      \"data\": {\n        \"services\": [\n          { \"type\": \"http\", \"local_port\": 3000, \"subdomain_or_path\": \"my-web-app\" },\n          { \"type\": \"http\", \"local_port\": 8080, \"subdomain_or_path\": \"api-service\" }\n          // Other types like 'tcp' might be supported by the agent protocol in the future.\n        ]\n      }\n    }\n    ```\n\n3.  **Server Response:** Upon successful registration, `Doroseek` responds with a `registered` message containing the unique `tunnelId` and the public base URL for accessing the tunneled services.\n    *Example `registered` message from server:*\n    ```json\n    {\n      \"type\": \"registered\",\n      \"data\": {\n        \"tunnelId\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n        \"public_base_url\": \"https://your-doroseek-instance/t/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n      }\n    }\n    ```\n\n**Accessing Tunneled Services:**\nOnce the tunnel is established, your local services can be accessed via the public URLs derived from the `public_base_url` and the `subdomain_or_path` defined during registration. For example, if `your-doroseek-instance` is `example.com` and a service was registered with `subdomain_or_path: 'my-web-app'`, it might be accessible via:\n```\nhttps://example.com/t/\u003ctunnelId\u003e/my-web-app/...\n```\nThe exact URL structure depends on how the public-facing routing (`/t/:tunnelId/...`) is configured to map to specific services.\n\n**Local Agent Responsibilities (High-Level):**\n*   Initiate and maintain the WebSocket connection to `/mcp/tunnel/register`.\n*   Send the `register` message with service definitions.\n*   Listen for `httpRequest` messages from `Doroseek` over the WebSocket. Each `httpRequest` message will contain:\n    *   `requestId`: A unique ID for the request.\n    *   `data`: An object with `method`, `path`, `headers`, and `body` of the incoming public request.\n*   Upon receiving an `httpRequest`, the agent makes a corresponding request to its local service (e.g., `http://localhost:3000/some/path`).\n*   Once the local service responds, the agent sends an `httpResponse` message back to `Doroseek` over the WebSocket, including the original `requestId` and the response details (status, headers, body).\n*   (WebSocket proxying through the tunnel is planned for future enhancements but is currently stubbed in `Doroseek`'s public-facing tunnel route).\n\n### 2. Multi-User Communication Rooms\n\n**Concept:**\nEnable real-time, bi-directional communication between multiple clients. This can be used as a backend for features like live chat in web applications, collaborative editing, or other multi-user interactive experiences.\n\n**Connecting to a Room:**\nClients connect to a room using a WebSocket connection. The `roomId` is specified in the path.\n\n*   **WebSocket Endpoint:**\n    ```\n    wss://your-doroseek-instance/mcp/room/:roomId?apiKey=\u003coptional_api_key\u003e\n    ```\n*   **Authentication:**\n    *   **Anonymous Access:** Currently, clients can connect without an API key.\n    *   **API Key Authenticated Access:** If an `apiKey` is provided in the query string, it is validated. This allows associating users with an API key owner if needed.\n\n**Core Functionality:**\n\n*   **Joining a Room:** A user joins a room by successfully establishing a WebSocket connection to the room's endpoint.\n    *   On join, the server sends a `ServerRoomInfoMessage` to the joining user, containing a list of users already in the room.\n    *   Other users in the room receive a `ServerUserJoinedMessage`.\n*   **Leaving a Room:** When a user's WebSocket connection is closed, they are automatically removed from the room.\n    *   Other users in the room receive a `ServerUserLeftMessage`.\n*   **Sending Chat Messages:** Clients send chat messages over the WebSocket.\n    *Example `ClientChatMessage` from client:*\n    ```json\n    {\n      \"type\": \"chatMessage\",\n      \"payload\": {\n        \"roomId\": \"your-target-room-id\", // Though roomId is in path, can be in payload\n        \"message\": \"Hello everyone!\"\n      }\n    }\n    ```\n*   **Receiving Messages:** Clients listen for messages from the server.\n    *   `ServerChatMessage`: A chat message sent by another user in the room.\n        ```json\n        {\n          \"type\": \"chatMessage\",\n          \"payload\": {\n            \"roomId\": \"the-room-id\",\n            \"fromUserId\": \"user-id-of-sender\",\n            \"message\": \"Hello everyone!\",\n            \"timestamp\": \"2023-01-01T12:00:00.000Z\"\n          }\n        }\n        ```\n    *   `ServerUserJoinedMessage`: Notifies that a new user has joined.\n    *   `ServerUserLeftMessage`: Notifies that a user has left.\n\n**Conceptual Client-Side Interaction Example (JavaScript):**\n```javascript\nconst roomId = \"my-chat-room\";\nconst apiKey = \"your-optional-api-key\"; // Or leave undefined for anonymous\nconst socket = new WebSocket(`wss://your-doroseek-instance/mcp/room/${roomId}?apiKey=${apiKey}`);\n\nsocket.onopen = () =\u003e {\n  console.log(\"Connected to room:\", roomId);\n  // Send a chat message\n  socket.send(JSON.stringify({\n    type: \"chatMessage\",\n    payload: { roomId, message: \"Hi from client!\" }\n  }));\n};\n\nsocket.onmessage = (event) =\u003e {\n  const serverMessage = JSON.parse(event.data);\n  console.log(\"Received message:\", serverMessage);\n  if (serverMessage.type === \"chatMessage\") {\n    // Display chat: serverMessage.payload.fromUserId, serverMessage.payload.message\n  } else if (serverMessage.type === \"userJoined\") {\n    // Update user list\n  } // etc.\n};\n\nsocket.onclose = () =\u003e {\n  console.log(\"Disconnected from room:\", roomId);\n};\n\nsocket.onerror = (error) =\u003e {\n  console.error(\"WebSocket error:\", error);\n};\n```\n\n### 3. Resource Sharing\n\n**Concept:**\nAllows users to upload small files or data snippets (e.g., configuration files, JSON data, small images) to `Doroseek` and share them via a unique, publicly accessible URL.\n\n**Upload Process (MCP WebSocket):**\n\n1.  **Endpoint:** Connect via WebSocket to `/mcp/fileshare/upload`.\n    ```\n    wss://your-doroseek-instance/mcp/fileshare/upload?apiKey=\u003cyour_api_key\u003e\n    ```\n    A valid API key is **required** for uploading files.\n\n2.  **Message Flow:**\n    *   **Client -\u003e Server: `ClientInitiateUploadMessage`**\n        The client first sends a message to initiate the upload, providing metadata about the file.\n        ```json\n        {\n          \"type\": \"initiateUpload\",\n          \"payload\": {\n            \"filename\": \"config.json\",\n            \"filetype\": \"application/json\",\n            \"size\": 1024 // Size in bytes\n          }\n        }\n        ```\n    *   **Server -\u003e Client: `ServerUploadReadyMessage`**\n        If the server accepts the upload (e.g., size is within limits), it responds with a unique `resourceId`.\n        ```json\n        {\n          \"type\": \"uploadReady\",\n          \"payload\": {\n            \"resourceId\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n          }\n        }\n        ```\n    *   **Client -\u003e Server: `ClientFileDataMessage`**\n        The client sends the actual file data, base64 encoded.\n        ```json\n        {\n          \"type\": \"fileData\",\n          \"payload\": {\n            \"resourceId\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n            \"data\": \"eyJrZXkiOiAidmFsdWUifQ==\" // Base64 encoded content of the file\n          }\n        }\n        ```\n    *   **Server -\u003e Client: `ServerUploadCompleteMessage`**\n        Upon successfully saving the file data, the server confirms completion and provides the download URL.\n        ```json\n        {\n          \"type\": \"uploadComplete\",\n          \"payload\": {\n            \"resourceId\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n            \"downloadUrl\": \"https://your-doroseek-instance/shared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n          }\n        }\n        ```\n\n**Download Process (HTTP GET):**\n\n*   **URL Format:** Files can be downloaded via a simple HTTP GET request to:\n    ```\n    https://your-doroseek-instance/shared/:resourceId\n    ```\n*   **Authentication:** By default, downloading does not require an API key. The link is publicly accessible if known.\n\n**Limitations:**\n*   **File Size Limit:** Currently, this feature is intended for **small files**. Due to the use of Deno KV as a backend for storing file data directly, individual file sizes are effectively limited to Deno KV's value size limit, which is typically around **60KB**. Uploads exceeding this will be rejected. Chunked uploading for larger files is a potential future enhancement.\n\n## Running locally\n\nThis section describes running `Doroseek` in its default **Server Mode**. For running `Doroseek` as a local agent for the Tunneling feature, see the [Agent Mode](#agent-mode) section.\n\n### Server Mode Configuration\n\ncopy .env.example to .env\n\n```sh\n# .env\nADMIN_KEY=the_admin_key_here\nMCP_MAX_DURATION=60\n```\n\nBy default, the project allows any key to create its corresponding settings, if\nyou need to restrict access to only a specific key, you need to set the\n`ADMIN_KEY` environment variable.\n\nThe MCP SSE connection can keep `MCP_MAX_DURATION` second.\n\nSet the db if needed\n\n```ts\n// services/database.ts\nexport const db = await Deno.openKv(\"./db\");\n```\n\n### Running in Server Mode\n\nTo run the app in its default server mode, ensure `DOROSEEK_AGENT_MODE_ENABLED` is not set to `\"true\"`.\nYou will need to install Deno. Then run from the root of this repository:\n\n```sh\ndeno task start\n```\n\n---\n\n## Agent Mode (for Intranet Penetration)\n\n`Doroseek` can also operate in **Agent Mode**. In this mode, it does not start the Fresh web server. Instead, it acts as a local client agent that connects to a remote `Doroseek` instance (acting as a relay server) to expose services from your private network to the internet.\n\nThis is an alternative running mode to its default server mode. When Agent Mode is enabled and configured correctly, `Doroseek` will exclusively perform agent duties.\n\n### Agent Mode Configuration\n\nTo enable and configure Agent Mode, you need to set the following environment variables:\n\n*   **`DOROSEEK_AGENT_MODE_ENABLED`**:\n    *   Set to `\"true\"` to enable Agent Mode. If this is not set to `\"true\"`, `Doroseek` will attempt to start in Server Mode.\n\n*   **`DOROSEEK_RELAY_URL`**:\n    *   The WebSocket URL of the remote `Doroseek` instance (acting as the relay server) to which this agent should connect. This URL should point to the relay's tunnel registration endpoint.\n    *   *Example:* `wss://your-remote-doroseek.com/mcp/tunnel/register`\n\n*   **`DOROSEEK_AGENT_API_KEY`**:\n    *   The API key that this agent will use to authenticate with the remote `Doroseek` relay server. This API key must be recognized and authorized by the relay server.\n\n*   **`DOROSEEK_AGENT_SERVICES_JSON`**:\n    *   A JSON string defining an array of local services that this agent should expose through the tunnel.\n    *   Each service object in the array must have the following fields:\n        *   `id` (string): A unique identifier for the service (chosen by you, e.g., \"my-web\").\n        *   `name` (string): A user-friendly name for the service (e.g., \"My Local Web Server\").\n        *   `type` (string): The type of service. Currently, only `\"http\"` is supported by the agent's HTTP handler.\n        *   `local_host` (string): The hostname or IP address of the local service (e.g., `\"localhost\"`, `\"127.0.0.1\"`).\n        *   `local_port` (number): The port number on which the local service is running (e.g., `8080`).\n        *   `subdomainOrPath` (string): A string that will be used by the relay server to form the public URL for this service. It should not contain `/` or spaces. It effectively becomes a path segment on the relay's public tunnel URL.\n            *   For example, if the relay provides a base URL like `https://\u003crelay-public-domain\u003e/t/\u003ctunnelId\u003e`, and you set `subdomainOrPath` to `\"myweb\"`, your service will be accessible at `https://\u003crelay-public-domain\u003e/t/\u003ctunnelId\u003e/myweb/...`.\n\n    *   **Example `DOROSEEK_AGENT_SERVICES_JSON` value:**\n        ```json\n        [\n          {\n            \"id\": \"web1\",\n            \"name\": \"My Local Web Server\",\n            \"type\": \"http\",\n            \"local_host\": \"localhost\",\n            \"local_port\": 8080,\n            \"subdomainOrPath\": \"myweb\"\n          },\n          {\n            \"id\": \"api2\",\n            \"name\": \"Backend API\",\n            \"type\": \"http\",\n            \"local_host\": \"127.0.0.1\",\n            \"local_port\": 3000,\n            \"subdomainOrPath\": \"myapi\"\n          }\n        ]\n        ```\n        To set this as an environment variable, the JSON string typically needs to be compact (no newlines) and properly escaped if set in certain shell environments. For example, in a `.env` file or shell:\n        `DOROSEEK_AGENT_SERVICES_JSON='[{\"id\":\"web1\",\"name\":\"My Local Web Server\",\"type\":\"http\",\"local_host\":\"localhost\",\"local_port\":8080,\"subdomainOrPath\":\"myweb\"},{\"id\":\"api2\",\"name\":\"Backend API\",\"type\":\"http\",\"local_host\":\"127.0.0.1\",\"local_port\":3000,\"subdomainOrPath\":\"myapi\"}]'`\n\n### Running in Agent Mode\n\n1.  Set all the required environment variables listed above. Ensure `DOROSEEK_AGENT_MODE_ENABLED` is set to `\"true\"`.\n2.  Run the application using the standard command:\n    ```sh\n    deno task start\n    ```\n3.  If the configuration is valid, `Doroseek` will start in Agent Mode. You will **not** see the usual Fresh server startup logs (e.g., \"Listening on http://localhost:8000/\").\n4.  Instead, look for log messages indicating Agent Mode operation, such as:\n    *   `[Main] Doroseek starting in Agent Mode.`\n    *   `[Agent Config] Agent configuration loaded successfully.`\n    *   `[Agent Connector] Initialized with Relay URL: wss://your-remote-doroseek.com/mcp/tunnel/register`\n    *   `[Agent Connector] Attempting to connect to ...`\n    *   `[Agent Connector] WebSocket connection established.`\n    *   `[Agent Connector] Sent registration request: ...`\n    *   `[Agent Connector] Successfully registered. Tunnel ID: \u003cyour-tunnel-id\u003e, Public URL: \u003cyour-public-base-url\u003e`\n    *   `[Main] Agent is connected and registered with the relay.`\n    *   `[Main] Tunnel ID: \u003cyour-tunnel-id\u003e`\n    *   `[Main] Public Base URL: \u003cyour-public-base-url\u003e`\n    *   `  - Service 'My Local Web Server' (web1) accessible via: \u003cyour-public-base-url\u003e/myweb`\n    *   `  - Service 'Backend API' (api2) accessible via: \u003cyour-public-base-url\u003e/myapi`\n\n### Interaction with the Relay (Agent Perspective)\n\n*   The agent (this `Doroseek` instance running in Agent Mode) establishes a WebSocket connection to the `DOROSEEK_RELAY_URL`, which should be the `/mcp/tunnel/register` endpoint of a remote `Doroseek` instance running in Server Mode.\n*   It sends a registration message detailing the local services defined in `DOROSEEK_AGENT_SERVICES_JSON`.\n*   The remote relay server, upon successful registration, provides the agent with a unique `tunnelId` and a `public_base_url`.\n*   Subsequently, when the relay server receives public HTTP requests matching the tunnel, it forwards these requests (as `httpRequest` messages) to the agent over the established WebSocket tunnel.\n*   The agent processes these `httpRequest` messages, makes requests to the configured local services, and sends `httpResponse` messages back to the relay.\n*   The agent also responds to `ping` messages from the relay with `pong` messages, indicating its status and the health of its primary local service.\n\n#### Tunnel Health Check API\n\nTo monitor the status of an established tunnel and the underlying local service, `Doroseek` provides a Health Check API endpoint.\n\n*   **Endpoint:** `GET /tunnel/:tunnelId/status`\n*   **Description:** This endpoint allows you to query the current status of a specific tunnel identified by `:tunnelId`. It checks both the WebSocket connection from the relay to the local agent and the agent's ability to reach its configured local service(s).\n*   **Authentication:** This endpoint does not currently require specific API key authentication, but the `tunnelId` must be valid and known to the system. Access control can be layered on top via standard reverse proxy or gateway mechanisms if needed.\n*   **Response Body (`HealthStatusReport`):**\n    The API returns a JSON object with the following structure:\n    ```json\n    {\n      \"tunnelId\": \"string\", // The ID of the tunnel checked\n      \"tunnelStatus\": \"connected\" | \"disconnected\" | \"unknown\",\n      \"localServiceStatus\": \"ok\" | \"error\" | \"unconfigured\" | \"timeout\" | \"unknown\" | \"agent_unresponsive\",\n      \"checkedByInstanceId\": \"string\", // ID of the relay instance that performed/reported the check\n      \"timestamp\": \"string\" // ISO 8601 timestamp of when the check was performed\n    }\n    ```\n    *   **`tunnelId`**: The ID of the tunnel that was checked.\n    *   **`tunnelStatus`**:\n        *   `\"connected\"`: The relay server has an active WebSocket connection to the local agent for this tunnel.\n        *   `\"disconnected\"`: The relay server does not have an active WebSocket connection to the local agent.\n        *   `\"unknown\"`: The status of the tunnel could not be determined (e.g., tunnel ID not found in the primary database).\n    *   **`localServiceStatus`**:\n        *   `\"ok\"`: The local agent responded to a health check ping and reported its primary configured local service is reachable.\n        *   `\"error\"`: The local agent responded, but reported an error when trying to check its local service.\n        *   `\"unconfigured\"`: The local agent responded, but has no local services configured to check, or the service type is not checkable by the agent.\n        *   `\"timeout\"`: The local agent responded, but its attempt to check the local service timed out.\n        *   `\"agent_unresponsive\"`: The relay server is connected to the agent, but the agent did not respond to a health check ping within the expected time.\n        *   `\"unknown\"`: The status of the local service could not be determined (e.g., because `tunnelStatus` is not `\"connected\"`).\n    *   **`checkedByInstanceId`**: The unique ID of the relay server instance that performed or last reported the health status. Useful in multi-instance deployments.\n    *   **`timestamp`**: The ISO 8601 timestamp of when the health status was determined.\n\n*   **Example Usage:**\n    Request:\n    ```\n    GET /tunnel/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/status\n    ```\n    Example Response:\n    ```json\n    {\n      \"tunnelId\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n      \"tunnelStatus\": \"connected\",\n      \"localServiceStatus\": \"ok\",\n      \"checkedByInstanceId\": \"yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\",\n      \"timestamp\": \"2023-10-27T10:30:00.000Z\"\n    }\n    ```\n    Another Example (Agent connected, but local service is down):\n    ```json\n    {\n      \"tunnelId\": \"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz\",\n      \"tunnelStatus\": \"connected\",\n      \"localServiceStatus\": \"error\",\n      \"checkedByInstanceId\": \"wwwwwww-wwww-wwww-wwww-wwwwwwwwwwww\",\n      \"timestamp\": \"2023-10-27T10:35:00.000Z\"\n    }\n    ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjunjiepro%2Fdoroseek","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjunjiepro%2Fdoroseek","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjunjiepro%2Fdoroseek/lists"}