From d985d658ca0c322c3c96e0709c28c1952dcafa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Thu, 3 Sep 2026 08:14:57 +0000 Subject: [PATCH] docs: document mobile remote access (#2807) (#3300) --- README.md | 4 +- docs/public/coverage.json | 1 + docs/public/meta.json | 1 + docs/public/mobile-remote-access.md | 231 ++++++++++++++++++++++++++++ docs/public/run-as-a-service.md | 4 +- docs/public/security.md | 2 +- docs/public/use-kandev.md | 4 +- docs/run-as-a-service.md | 2 + 8 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 docs/public/mobile-remote-access.md diff --git a/README.md b/README.md index 262ed1b8d8..8b77d18250 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Manage and run tasks in parallel. Orchestrate agents. Review changes. Ship value. -[Features](docs/features.md) | [Workflows](docs/workflow-tips.md) | [Run as a Service](docs/run-as-a-service.md) | [Debug Logs](docs/debug-logs.md) | [Roadmap](docs/roadmap.md) | [Contributing](CONTRIBUTING.md) | [Architecture](docs/ARCHITECTURE.md) | [Discord](https://discord.gg/gWdCPGcFCD) +[Features](docs/features.md) | [Workflows](docs/workflow-tips.md) | [Run as a Service](docs/run-as-a-service.md) | [Mobile Access](docs/public/mobile-remote-access.md) | [Debug Logs](docs/debug-logs.md) | [Roadmap](docs/roadmap.md) | [Contributing](CONTRIBUTING.md) | [Architecture](docs/ARCHITECTURE.md) | [Discord](https://discord.gg/gWdCPGcFCD)

Kandev Demo @@ -17,7 +17,7 @@ Kandev is a powerful tool for power users who want deeper control over how AI ag Organize work across kanban and pipeline views with opinionated workflows and execute multiple tasks in parallel. Assign agents from any provider, and review their output in an integrated workspace - file editor, file tree, terminal, browser preview, and git changes in one place. Terminal agent TUIs are great for running agents, but reviewing and iterating on changes there doesn't scale. -Run it locally or self-host it on your own infrastructure and access it from anywhere via [Tailscale](https://tailscale.com/) or any VPN. +Run it locally or self-host it on your own infrastructure. Use the [mobile remote-access guide](docs/public/mobile-remote-access.md) to connect through Tailscale, Cloudflare Tunnel, or another private VPN. Open source, multi-provider, no telemetry, not tied to any cloud. diff --git a/docs/public/coverage.json b/docs/public/coverage.json index a96c0dc4ad..eb2e8ef85e 100644 --- a/docs/public/coverage.json +++ b/docs/public/coverage.json @@ -430,6 +430,7 @@ "stability": "stable", "docs": [ "security", + "mobile-remote-access", "configuration", "executors", "automation-and-mcp", diff --git a/docs/public/meta.json b/docs/public/meta.json index 2590a7f328..72d7820f30 100644 --- a/docs/public/meta.json +++ b/docs/public/meta.json @@ -25,6 +25,7 @@ "---Deploy and Operate---", "docker", "run-as-a-service", + "mobile-remote-access", "windows-support", "k8s", "operations", diff --git a/docs/public/mobile-remote-access.md b/docs/public/mobile-remote-access.md new file mode 100644 index 0000000000..593e28054d --- /dev/null +++ b/docs/public/mobile-remote-access.md @@ -0,0 +1,231 @@ +--- +title: "Mobile Remote Access" +description: "Use Kandev from a phone through Tailscale, Cloudflare Tunnel, or a private VPN." +--- + +# Use Kandev from a Phone + +This how-to guide connects a phone to Kandev through Tailscale, Cloudflare Tunnel, or a private VPN. + +Anyone who can reach an unauthenticated Kandev origin has administrator access. Use a protected network boundary. + +## How a phone request reaches an agent + +Each protected path reaches the same Kandev origin. Kandev then sends task work to the selected executor. + +```mermaid +flowchart LR + Phone["Phone browser"] + Tailscale["Tailscale Serve"] + Cloudflare["Cloudflare Access"] + Cloudflared["cloudflared"] + VPN["Private VPN"] + Kandev["Kandev
HTTP and WebSocket"] + Session["Task and agent session"] + Executor["Selected executor"] + Repo["Repository"] + + Phone --> Tailscale --> Kandev + Phone --> Cloudflare --> Cloudflared --> Kandev + Phone --> VPN --> Kandev + Kandev --> Session --> Executor --> Repo +``` + +The access path protects the Kandev origin. Each path reaches the same Kandev web app and task features. +Kandev authentication remains a separate user and workspace boundary. + +## Choose an access boundary + +Use one of these boundaries: + +| Use case | Network boundary | Kandev authentication | +| --- | --- | --- | +| One person, trusted phone and host | Private tailnet with a narrow Tailscale grant | Optional | +| One person, another private VPN | Private VPN address plus host firewall | Optional | +| One person, Cloudflare Tunnel | Cloudflare Access policy for one trusted identity | Optional | +| Multiple people or less-trusted devices | Private network or authenticated TLS proxy | Required | +| Public internet | Do not expose Kandev directly | Required behind a protected TLS proxy | + +When authentication is disabled, Kandev gives every reachable client one synthetic administrator identity. + +Enable [Authentication and Users](authentication.md) for accounts, private workspaces, sessions, or personal access tokens. + +## Connect with Tailscale + +This path keeps Kandev on loopback. Tailscale Serve provides the tailnet listener and HTTPS endpoint. + +1. [Install Tailscale](https://tailscale.com/docs/install) on the Kandev host and phone. +2. Sign in to the same tailnet on both devices. +3. If the tailnet does not use [MagicDNS](https://tailscale.com/docs/features/magicdns), enable it. +4. Start Kandev on a fixed loopback port. + +```bash +KANDEV_SERVER_HOST=127.0.0.1 KANDEV_BACKEND_PORT=38429 kandev +``` + +5. In another host terminal, publish that loopback service inside the tailnet. + +```bash +tailscale serve --bg http://127.0.0.1:38429 +tailscale serve status +``` + +6. Open the HTTPS URL that `tailscale serve` prints on the phone. + +The URL uses the full MagicDNS name of the host, such as `https://kandev-host.example.ts.net`. + +[Tailscale Serve](https://tailscale.com/docs/reference/tailscale-cli/serve) terminates HTTPS and keeps the endpoint inside the tailnet. Do not use Tailscale Funnel because Funnel publishes the endpoint to the internet. + +If Kandev runs as a managed service, use the [service guide](run-as-a-service.md). Make sure that the backend port remains fixed before you keep a Serve rule. + +### Restrict the tailnet connection + +Tailscale recommends grants for new access-control policies. Legacy ACL rules continue to work. + +Tag the Kandev host as `tag:kandev`. Then add a grant like this to the tailnet policy: + +```json +{ + "tagOwners": { + "tag:kandev": ["you@example.com"] + }, + "grants": [ + { + "src": ["you@example.com"], + "dst": ["tag:kandev"], + "ip": ["tcp:443"] + } + ] +} +``` + +Replace `you@example.com` with the identity that owns the phone. Keep other required rules in the same policy file. + +The grant permits only that identity to reach HTTPS on tagged Kandev hosts. Read the [Tailscale grants reference](https://tailscale.com/docs/reference/syntax/grants) before you replace an existing policy. + +## Connect with Cloudflare Tunnel + +Cloudflare Tunnel gives Kandev a public HTTPS hostname without a public origin address. The `cloudflared` connector makes an outbound connection from the Kandev host. + +The hostname is public unless Cloudflare Access protects it. Create the Access application before you add the tunnel route. + +### Protect the hostname with Cloudflare Access + +1. Add your domain to Cloudflare and create a Cloudflare Zero Trust organization. +2. In the Cloudflare dashboard, go to **Zero Trust > Access controls > Applications**. +3. Select **Create new application**. +4. Select **Self-hosted and private**. +5. Add a public hostname, such as `kandev.example.com`. +6. Add an **Allow** policy for the identity that can use Kandev. +7. Select the required identity provider or one-time PIN method. +8. Create the application. + +Access denies requests that do not match an Allow policy. Read the [Cloudflare self-hosted application guide](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for policy details. + +For one trusted person, Cloudflare Access can be the external access boundary. If the policy allows multiple people, also enable [Kandev authentication](authentication.md). + +Cloudflare identities do not become Kandev accounts. Without Kandev authentication, all permitted Access users share the synthetic Kandev administrator. + +### Start Kandev on loopback + +Start Kandev on a fixed loopback port: + +```bash +KANDEV_SERVER_HOST=127.0.0.1 KANDEV_BACKEND_PORT=38429 kandev +``` + +If Kandev runs as a service, configure the same loopback address and fixed port. + +Follow [Bind safely](run-as-a-service.md#bind-safely) and the [fixed-port workaround](run-as-a-service.md#fixed-port-operator-workaround). + +### Install and configure `cloudflared` + +1. In the Cloudflare dashboard, go to **Networking > Tunnels**. +2. Create a tunnel and select the `cloudflared` connector. +3. Select the operating system and architecture of the Kandev host. +4. Copy the installation command that Cloudflare provides. +5. Run the command on the Kandev host. + +The service command has this form: + +```bash +sudo cloudflared service install +``` + +The tunnel token is a secret. Do not commit it or paste it into Kandev tasks, logs, or chat. + +Only one `cloudflared` service can run on a host. If one exists, add the Kandev route to its tunnel. + +6. Add a **Public hostname** route for `kandev.example.com`. +7. Select `HTTP` as the service type. +8. Enter `127.0.0.1:38429` as the service URL. +9. Enable **Protect with Access** for the route. +10. Save the route and wait for the connector to become healthy. +11. Open `https://kandev.example.com` on the phone. +12. Complete the Cloudflare Access sign-in. + +Keep the default HTTP Host header so browser requests use the public hostname. + +[Cloudflare Tunnel supports WebSockets](https://developers.cloudflare.com/cloudflare-one/faq/cloudflare-tunnels-faq/#does-cloudflare-tunnel-support-websockets). The web app, composers, live updates, and terminal connections can use the same protected hostname. + +Read the [Cloudflare Tunnel guide](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/) for connector installation and network requirements. + +## Connect directly through a VPN + +If the VPN gives the host a stable address, bind Kandev to that address: + +```bash +KANDEV_SERVER_HOST=100.100.100.100 KANDEV_BACKEND_PORT=38429 kandev +``` + +Replace the example address with the private VPN address. Then open `http://100.100.100.100:38429` from the connected phone. + +Some VPNs require an all-interface bind: + +```bash +KANDEV_SERVER_HOST=0.0.0.0 KANDEV_BACKEND_PORT=38429 kandev +``` + +`0.0.0.0` also listens on LAN and public interfaces. A Tailscale grant does not control traffic that arrives outside Tailscale. + +If you use this bind, permit port `38429` only on the VPN interface in the host firewall. Reject that port on every other interface. + +If more than one person can reach the VPN endpoint, use [Kandev authentication](authentication.md) and TLS. + +Kandev authentication does not replace HTTPS. For single-user access on an encrypted VPN, the VPN tunnel provides transport encryption. +Plain HTTP inside that tunnel is acceptable. + +## Add Kandev to the home screen + +Kandev includes an installable web-app manifest and standalone display mode. The installed shortcut opens Kandev without a normal browser tab bar. + +Use an HTTPS Kandev URL for standalone installation. The Tailscale Serve and Cloudflare Tunnel paths provide HTTPS. Direct HTTP access may create only a home-screen shortcut, not an installable standalone web app. + +On iPhone or iPad: + +1. Open the Kandev URL in Safari. +2. Select **Share**. +3. Select **Add to Home Screen**. +4. Open Kandev from the new icon. + +On Android: + +1. Open the Kandev URL in Chrome. +2. Open the browser menu. +3. Select **Install app** or **Add to Home screen**. +4. Open Kandev from the new icon. + +The shortcut does not add offline support. The Kandev host and VPN must remain reachable. + +## Troubleshoot the connection + +1. On the Kandev host, run `tailscale status`. On iOS or Android, check the Tailscale app instead. +2. If you use Serve, run `tailscale serve status` on the Kandev host. +3. For Cloudflare Tunnel, review the connector status in the Cloudflare dashboard. +4. Open `/ready` on the same Kandev origin. +5. Review the host firewall and the tailnet or Access policy. +6. Review the actual Kandev port in the startup output or service logs. + +If the page loads but live updates fail, make sure that the proxy forwards the whole origin and supports WebSockets. + +See [Security and Trust](security.md) for the complete boundary. diff --git a/docs/public/run-as-a-service.md b/docs/public/run-as-a-service.md index ef1494914d..328ba5364c 100644 --- a/docs/public/run-as-a-service.md +++ b/docs/public/run-as-a-service.md @@ -13,7 +13,7 @@ Stable is the default release channel. A verified Kandev-managed npm/npx user se the npm Nightly channel from **Settings → System → Updates**. Desktop, Homebrew, and system services remain Stable-only. -> **Network security:** the backend listens on `0.0.0.0` by default and ships with authentication **disabled**. Before allowing remote access, enable [opt-in authentication](authentication.md) (the **Authentication & users** feature toggle, or `KANDEV_FEATURES_AUTH=true`) and terminate TLS in a reverse proxy; authentication does not replace HTTPS. A server bound to non-loopback interfaces without authentication logs a startup warning. See [server configuration](configuration.md#root-and-server). +> **Network security:** the backend listens on `0.0.0.0` by default and ships with authentication **disabled**. For one trusted user, use the narrow private-network boundary in [Mobile Remote Access](mobile-remote-access.md). For shared access, enable [opt-in authentication](authentication.md) and terminate TLS in a protected proxy. Authentication does not replace HTTPS. A non-loopback server without authentication logs a startup warning. See [server configuration](configuration.md#root-and-server). ## Quick path @@ -118,7 +118,7 @@ To access a loopback-only instance remotely, use SSH port forwarding: ssh -L 38429:127.0.0.1:38429 user@server ``` -Then open `http://127.0.0.1:38429` locally. For shared access, terminate TLS and enforce authentication in a reverse proxy or private access layer. +Then open `http://127.0.0.1:38429` locally. For private phone access, follow [Mobile Remote Access](mobile-remote-access.md). For shared access, terminate TLS and enforce authentication in a proxy or private access layer. ## Commands and flags diff --git a/docs/public/security.md b/docs/public/security.md index fe9955b728..966d4155b7 100644 --- a/docs/public/security.md +++ b/docs/public/security.md @@ -38,7 +38,7 @@ For remote access, protect the whole origin, including: - Streamable HTTP MCP at `/mcp`; and - SSE compatibility at `/mcp/sse` and `/mcp/message`. -Use an authenticated reverse proxy that supports WebSockets and long-lived streaming, or keep the service on a private VPN. Block direct access to the backend so a client cannot bypass the proxy. See [Run Kandev as a service](run-as-a-service.md), [Docker](docker.md), and [Kubernetes](k8s.md) for deployment-specific constraints. +Use an authenticated reverse proxy that supports WebSockets and long-lived streaming, or keep the service on a private VPN. Block direct access to the backend so a client cannot bypass the proxy. See [Mobile Remote Access](mobile-remote-access.md), [Run Kandev as a service](run-as-a-service.md), [Docker](docker.md), and [Kubernetes](k8s.md) for deployment-specific constraints. ## Understand executor access diff --git a/docs/public/use-kandev.md b/docs/public/use-kandev.md index 5847debaf9..39dbb0262b 100644 --- a/docs/public/use-kandev.md +++ b/docs/public/use-kandev.md @@ -74,7 +74,7 @@ The launcher selects the platform runtime, starts the Go backend and agent runti By default, persistent state is under `~/.kandev`, including the SQLite database, repository materializations, sessions, logs, and backups. `KANDEV_HOME_DIR` relocates that root. `KANDEV_DATABASE_PATH` overrides the database file and moves System snapshots to `backups/` beside that file. Kandev creates its data directory with owner-only permissions and rejects symlinked components in that path, but file permissions do not replace host access controls. Kandev does not move snapshots from another directory automatically. -See the [CLI reference](cli.md) for commands, port and logging flags, data paths, environment variables, and update behavior. Other supported entry points are the [desktop app](desktop-app.md), [service](run-as-a-service.md), and [Docker deployment](docker.md). Read [Security and trust](security.md) before making any installation remotely reachable. +See the [CLI reference](cli.md) for commands, port and logging flags, data paths, environment variables, and update behavior. Other supported entry points are the [desktop app](desktop-app.md), [service](run-as-a-service.md), and [Docker deployment](docker.md). Follow [Mobile Remote Access](mobile-remote-access.md) for a private phone connection. Read [Security and trust](security.md) before making any installation remotely reachable.

First-run records and onboarding details @@ -199,7 +199,7 @@ See [Sessions and review](sessions-and-review.md) for the workbench and [Tasks a - Managed GitHub task access (the default) uses task/repository-bound broker leases from the workspace automation connection. GitHub App tokens are minted for the redeemed repository; PAT and named-CLI bearer tokens retain all provider-granted scopes once delivered to the trusted agent subprocess. Personal tokens and the App private key never enter executors. Choose **Inherit executor Git credentials** in the workspace GitHub settings only when task Git and `gh` should use host-visible credentials for Local/Worktree or intentionally configured credentials for remote executors. - A profile-supplied `GITHUB_TOKEN` or `GH_TOKEN` is an explicit unmanaged override and bypasses workspace broker selection. Ambient backend tokens and the host-active `gh` account are used only by migration-only **Legacy shared** workspace connections. - Repository, executor, and task action scripts are executable configuration. Review them like code. -- The web app, HTTP API, WebSocket, and external MCP routes currently have no Kandev user-login boundary. Treat anyone who can reach the backend as an operator; keep the whole origin on loopback or a trusted network, or put it behind an authenticated TLS proxy. +- Authentication is opt-in. When it is disabled, treat anyone who can reach the backend as an operator. Keep the whole origin on loopback or a trusted network, or put it behind an authenticated TLS proxy. Use the [Security and trust](security.md) guide to choose a local, remote, shared-team, or unattended-automation boundary and to review the deployment checklist. diff --git a/docs/run-as-a-service.md b/docs/run-as-a-service.md index 29455daf66..190febb3a7 100644 --- a/docs/run-as-a-service.md +++ b/docs/run-as-a-service.md @@ -24,6 +24,8 @@ sudo kandev service install --system After install, kandev is reachable at `http://localhost:38429` (or `--port ` if you passed it). +For phone access through Tailscale, Cloudflare Tunnel, or another VPN, follow [Mobile Remote Access](public/mobile-remote-access.md). + ## Run the Current Checkout as a Service When developing from a cloned repo, use the root Make targets instead of the globally installed `kandev` binary. They install dependencies, build the currently checked-out branch, assemble a local release-style bundle under `dist/kandev`, and install the service with `KANDEV_BUNDLE_DIR` pointing at that bundle.