Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

21 KiB

title, description
title description
Remote Access Access AgentsView from other devices on your network

AgentsView binds to 127.0.0.1 by default, so only your local machine can reach it. To make the UI available from other devices, you need to do two things:

  • listen on a non-loopback address or put the server behind a proxy
  • require a bearer token for API access

In current releases, bearer-token auth is controlled by require_auth.

Quick Setup

For a one-off background server, enable auth in ~/.agentsview/config.toml:

require_auth = true

Then pass a one-off non-loopback bind:

agentsview serve --background --host 0.0.0.0

Omit --background when you want that serve invocation to stay in the foreground. The explicit --host flag also permits a one-off unauthenticated non-loopback bind, though authenticated access is strongly recommended.

For a persistent, config-driven node — so restarts and auto-started daemons keep the server reachable — set both values in config.toml instead of passing a flag each time:

host = "0.0.0.0"
require_auth = true

Then use the canonical writable-daemon lifecycle:

agentsview daemon start
agentsview daemon status
# After configuration changes:
agentsview daemon restart

A non-loopback host in config.toml requires require_auth = true; the server refuses to start rather than persistently exposing an unauthenticated API. The --host flag remains available for one-off unauthenticated binds.

When the server runs inside WSL, AgentsView advertises the WSL eth0 address instead of 127.0.0.1 so the printed URL is usable from the Windows host and nearby LAN clients. An explicit --public-url still takes precedence.

When auth is enabled, AgentsView generates a token if needed and stores it in ~/.agentsview/config.toml. Open http://<your-ip>:8080 from another device and enter the configured token in the frontend, or send it in an Authorization header.

!!! note

Older configs may still use `remote_access = true`. AgentsView still reads
that legacy key, but new setups should use `require_auth = true`.

Authentication

When require_auth is enabled, all /api/ requests must include:

Authorization: Bearer <token>

The token is stored in config.toml as auth_token. It is generated automatically the first time auth is enabled, so you do not need to mint one manually. For supervised services or temporary overrides, set AGENTSVIEW_AUTH_TOKEN; it takes precedence over the config file.

Auth is enforced on API routes, not on static assets. That means a browser can load the HTML shell, but API requests fail with 401 until the correct token is supplied.

When require_auth is disabled, normal local loopback use remains ungated.

HTTP Remote Sync

Configured [[remote_hosts]] entries can use transport = "http" when the remote machine is already running an AgentsView daemon:

[[remote_hosts]]
host = "devbox1"
transport = "http"
url = "http://devbox1.tailnet.ts.net:8080"
token = "remote-token"
interval = "5m" # optional: sync periodically while the collector daemon runs

Treat host as the remote machine's stable, unique identity. AgentsView uses it to namespace imported session IDs, the database skip cache, and the mirror directory. Changing it creates a new namespace and can duplicate sessions from the same machine; reusing it for a different machine can reuse stale cached state. Changing only url is fine when the same logical machine moves.

The daemon on the remote machine must bind a non-loopback interface (set host = "0.0.0.0" with require_auth = true in its config.toml) or every sync fails with a connection-refused error. See Remote Hosts for the full remote-side setup, including keeping detached daemons alive.

Use require_auth = true on remote nodes when practical, or at minimum keep their generated auth_token configured. The remote archive endpoints always require Authorization: Bearer <token>, even when the rest of the daemon API is unauthenticated. The token in the collector's remote_hosts entry must match the remote daemon's auth_token and is required for HTTP remote sync. Do not reuse the collector daemon's own auth_token for untrusted remote endpoints.

The HTTP transport is intended for private networking such as Tailscale or an equivalent restricted overlay. Do not expose raw archive endpoints directly to the public internet.

HTTP remote sync failures are summarized without echoing remote-controlled URLs or response bodies. Common summaries point at the specific fix: a rejected token means the collector's per-host token does not match the remote daemon's auth_token; a missing endpoint means the remote host needs a newer AgentsView; connection refusal usually means the daemon is not running, is still bound to loopback, or the URL port is wrong; DNS and timeout messages point back to the configured url.

For always-available fleet nodes launched with agentsview daemon start, set:

daemon_idle_timeout = "0s"

That setting controls detached writable daemons, including config-driven and serve --background launches. Supervised daemons run under systemd, launchd, Docker, or a foreground shell never create the idle tracker and already stay alive until their supervisor stops them.

Run agentsview sync on the collector to sync local sessions and every configured host. Set interval on a [[remote_hosts]] entry when a running collector daemon should sync that host periodically; omit it or set it to 0s for manual sync only. See agentsview sync for single-host selection and failure behavior.

Incremental Sync

Starting in 0.37.4, HTTP remote sync keeps a persistent mirror of each remote machine's syncable source files under:

<data_dir>/remote-mirrors/<sanitized-host>-<hash>/

For the default data directory, that parent is ~/.agentsview/remote-mirrors/. The readable host component is followed by a hash so names that sanitize to the same directory name do not collide. A lock file next to each mirror serializes concurrent syncs of that host from the same data directory.

The mirror adds an on-disk copy of the remote session sources to the collector, in addition to the indexed database. Budget roughly the size of each remote host's syncable source corpus for it. Incremental transfer applies only to the HTTP transport; SSH remote sync continues to copy a full session tree on each run.

How A Sync Works

  1. The collector asks the remote daemon for its resolved agent roots. The remote controls this allowlist; the collector cannot name arbitrary roots.
  2. The collector locks the host mirror and requests a gzip-compressed manifest of regular, non-symlink files. Each entry carries its absolute remote path, size, and modification time.
  3. The collector walks the mirror and compares file size and modification time at microsecond precision. It schedules missing or changed files for fetch and removes mirror files that disappeared from the manifest.
  4. When fewer than half of the manifest's files need fetching, the collector requests only that delta. At half or more, it requests a full archive instead of sending a large file list. The current collector advertises gzip support for both archive modes.
  5. The archive is extracted into the mirror with remote modification times preserved. AgentsView then imports from the complete mirror, so parsers that read sibling files behave the same way they do against a full source tree. The separate database skip cache avoids unnecessary parsing of unchanged sessions during normal syncs.

For a configured full sync that includes local sources, mirror preparation finishes before database work begins. The collector then ingests local sources and every prepared HTTP mirror through the same batched temporary-database path, with FTS maintenance suspended during ingest and rebuilt once before an atomic swap. Configured SSH hosts run afterward. A preparation, parser, batch-write, or FTS failure leaves the active archive unchanged and prevents the SSH phase.

Remote-only syncs, including agentsview sync --host <configured-http-host> --full, continue to import into the active archive and do not use the combined rebuild path.

If no directory-scoped files changed, the collector skips the archive request and imports directly from the existing mirror. Files that disappear remotely are deleted from the mirror, but remote import is intentionally non-destructive: sessions already stored in the AgentsView database remain available.

Windsurf is a special case. Its state database is sanitized into a curated export for every transfer, so the raw tree cannot safely participate in the manifest. AgentsView fetches that small export as a separate full archive on each sync while the rest of the host remains eligible for delta transfer. The Windsurf content in the mirror is the sanitized export, not a byte-for-byte copy of the remote state database.

When AgentsView Downloads A Full Archive

A full HTTP transfer occurs in these cases:

  • the per-host mirror is new or was removed
  • at least half of the manifest files are missing or changed
  • a delta request is rejected after a manifest succeeded; the collector retries once with a full archive
  • the remote daemon does not support manifests, in which case the collector uses the legacy full-transfer path on every sync

Windsurf's curated export is also fetched in full on every sync, independently of the directory-scoped archive decision.

--full reparses every discovered remote session but still uses the manifest comparison to decide which mirror bytes need transferring. It does not delete the local database or turn remote sync into a destructive reconciliation.

Compatibility And Recovery

A current collector works with older remote daemons that already expose the HTTP remote-sync target and archive endpoints. A missing manifest route — including an old daemon's HTML app shell answering that route — makes the collector report that incremental transfer is unavailable and use the legacy full-archive flow. That flow extracts to a temporary directory and does not create or update the persistent mirror. During a configured full local sync, the temporary source still uses the collector's new batched ingest path.

Therefore the collector can be upgraded and tested before its spokes. Upgrading only the collector provides the database-ingest speedup; upgrading each spoke adds manifest-delta transfer and avoids downloading its complete archive. A spoke old enough to lack the target or archive endpoints was not compatible with HTTP remote sync before this change either. Older collectors also continue to use the full-archive endpoint on a current remote.

The normal mirror comparison detects interrupted extraction when the resulting file size or modification time differs from the manifest, and the next sync fetches that file again. It also repairs file-versus-directory conflicts left by an interrupted extraction.

The comparison does not hash file content. A remote rewrite that preserves both size and modification time, or local mirror corruption with the same metadata, can therefore look unchanged. Because --full now separates reparsing from mirror transfer, it does not repair same-stat corruption by itself.

The mirror is a disposable transfer cache. When no sync is running, deleting a host's mirror directory is the repair procedure: the next compatible sync bootstraps it again. Leave the adjacent .lock file in place. Removing mirror files never removes imported sessions from the database.

Transfer Safety

The remote daemon recomputes its allowed sync targets for each request and rejects paths outside them. Manifest walks omit symlinks and special files; delta requests must either match an allowed extra file exactly or use an absolute path in the same POSIX, drive-letter, or UNC dialect as the allowed root. Symlinked roots or intermediate components are refused. Mirror deletions and type-conflict cleanup are separately confined to the per-host mirror root.

These checks limit what authenticated sync requests can read, but they do not replace network isolation or bearer-token security. Keep the daemon on a private network and protect its auth_token as described above.

SSE Endpoints

The SSE endpoints also accept ?token=<token> because browser EventSource cannot set custom headers:

http://<host>:8080/api/v1/events?token=<token>
http://<host>:8080/api/v1/sessions/<id>/watch?token=<token>

Use header-based auth for normal API calls whenever possible.

Public URL And Trusted Origins

When you access AgentsView through a hostname or reverse proxy, tell the server about the public URL:

agentsview serve --public-url https://agents.example.com

For additional trusted origins, use --public-origin:

agentsview serve \
  --public-origin https://agents.example.com \
  --public-origin https://internal.example.com

Flags can also be comma-separated:

agentsview serve --public-origin https://a.example.com,https://b.example.com

These can also be persisted:

public_url = "https://agents.example.com"
public_origins = [
  "https://agents.example.com",
  "https://internal.example.com",
]

Forwarded Dev Environments

AgentsView validates the request Host header before serving API requests. That protects local loopback servers from DNS-rebinding attacks, but it also means SSH port forwards, reverse proxies, WSL2, Codespaces, Coder, and similar remote development environments must use a trusted public URL or origin.

Static assets can still load in these environments because the HTML shell is intentionally less strict than /api/ routes. The dashboard may appear briefly, then /api/v1/settings or another API request can fail with 403 Forbidden if the browser Host is the forwarded hostname rather than one of the local allowed hosts.

Restart the server with the exact origin you open in the browser:

# exe.dev browser URL: https://<vm>.exe.xyz
agentsview serve --public-url https://<vm>.exe.xyz

# ssh -L 18080:127.0.0.1:8080 host
# Browser opens http://127.0.0.1:18080
agentsview serve --public-url http://127.0.0.1:18080

--public-url must match the browser origin exactly, including the scheme, hostname, and non-default port. When the browser sends an untrusted Host, current releases return 403 Forbidden with a response body that explains the rejected host, the allowed set, and the --public-url fix. The Settings page shows the same actionable message instead of prompting for an auth token. A short breadcrumb is also written to the server debug log.

Troubleshooting Forwarded Access

If the UI flashes and then the dashboard or settings page fails, check the /api/v1/settings request in browser devtools:

Symptom or check Meaning Fix
/api/v1/settings returns 401 Unauthorized Bearer-token auth is enabled Use the token printed by the server or stored in ~/.agentsview/config.toml
/api/v1/settings returns 403 Forbidden The browser host or origin was rejected Restart with agentsview serve --public-url <exact-browser-origin>
/api/v1/settings goes to an unexpected host The frontend has a saved remote server URL Clear localStorage.getItem("agentsview-server-url") for this site

To clear an unintended saved server URL, run this in the browser console and reload:

localStorage.removeItem("agentsview-server-url")
location.reload()

Managed Caddy Mode

AgentsView can manage a Caddy reverse proxy for TLS-terminated access. The AgentsView backend stays on loopback while Caddy handles the public socket.

!!! warning "Enable auth first"

Managed Caddy exposes a public endpoint. Set `require_auth = true` in
`~/.agentsview/config.toml` before starting the server, or the proxy will
front an unauthenticated API. See [Quick Setup](#quick-setup).
agentsview serve \
  --public-url https://agents.example.com \
  --proxy caddy \
  --tls-cert /path/to/cert.pem \
  --tls-key /path/to/key.pem
Flag Default Description
--proxy Proxy mode — currently caddy
--caddy-bin caddy Path to the Caddy binary
--proxy-bind-host 127.0.0.1 Interface for Caddy to bind
--public-port 8443 External port for the public URL
--tls-cert TLS certificate file path
--tls-key TLS key file path
--allowed-subnet Client CIDR allowlist (repeatable or comma-separated)

Caddy must already be installed and available on PATH unless you override it with --caddy-bin.

Subnet Allowlists

When using a non-loopback bind host for Caddy, restrict access to specific networks. Subnet allowlists complement bearer-token auth rather than replacing it — require_auth = true should still be set before binding Caddy off loopback.

agentsview serve \
  --proxy caddy \
  --proxy-bind-host 0.0.0.0 \
  --allowed-subnet 192.168.1.0/24 \
  --allowed-subnet 10.0.0.0/8

Requests from outside the allowed subnets receive 403.

Settings Page

Remote access can also be configured from the Settings page. The Remote Access section lets you:

  • toggle require_auth
  • view the auto-generated auth token
  • connect the frontend to another AgentsView server by URL and token

Settings remote access

Changes that affect bind or auth behavior may require a server restart.

CLI Flags Reference

Flag Default Description
--host 127.0.0.1 Interface to bind
--require-auth false Require a bearer token for API requests
--public-url Public URL for hostname or proxy access
--public-origin Trusted browser origin (repeatable/comma-separated)
--proxy Managed proxy mode (caddy)
--caddy-bin caddy Caddy binary path
--proxy-bind-host 127.0.0.1 Interface for managed proxy
--public-port 8443 External port for managed proxy
--tls-cert TLS certificate path
--tls-key TLS key path
--allowed-subnet Client CIDR allowlist (repeatable/comma-separated)

Config File Reference

Remote-access-related fields in ~/.agentsview/config.toml:

require_auth = true
auth_token = "auto-generated-base64-token"
public_url = "https://agents.example.com"
public_origins = ["https://agents.example.com"]

[proxy]
mode = "caddy"
bin = "caddy"
bind_host = "127.0.0.1"
public_port = 8443
tls_cert = "/path/to/cert.pem"
tls_key = "/path/to/key.pem"
allowed_subnets = ["192.168.1.0/24"]
Field Description
host Server bind interface; non-loopback values require require_auth = true
require_auth Require bearer-token authentication for API access
auth_token Auto-generated 256-bit bearer token; overridden by AGENTSVIEW_AUTH_TOKEN
public_url Public URL for host/origin validation
public_origins Additional trusted CORS origins
proxy.mode Managed proxy mode (caddy)
proxy.bin Path to proxy binary
proxy.bind_host Interface for proxy to bind
proxy.public_port External port for proxy
proxy.tls_cert TLS certificate path
proxy.tls_key TLS key path
proxy.allowed_subnets CIDR allowlist for proxy connections

For LAN access you still need a non-loopback bind such as agentsview serve --host 0.0.0.0. require_auth controls API auth; it does not change the bind address by itself.