(AI generated. Not reviewed.)
Tailscale private transport for Fichero
This is the supported model for using one Fichero engine from another device on
your tailnet: keep the engine bound to loopback, then let tailscale serve
publish a tailnet-private HTTPS proxy to that loopback service.
Do not bind the engine to 0.0.0.0, a LAN address, or a Tailscale 100.x
address for normal remote access. Do not use tailscale funnel. Funnel exposes
the service on the public internet, and the Fichero engine is not a public web
service.
Trust boundary
Tailscale is transport security. It answers “can this device reach the service over the tailnet?” It does not answer “can this Fichero user read or edit this library, folder, claim, or document?”
App-level authorization still belongs to Fichero:
- The shared-secret API token is still required for protected endpoints.
- Multi-user authorization, when enabled with
FICHERO_MULTIUSER=1, still owns per-library and per-folder permissions. - Tailscale ACLs can reduce which devices can reach the engine, but they are not a substitute for object-level app authorization.
Start the engine
On the Mac or lab machine that owns the library, start the engine on loopback:
PYTHONPATH=fichero-server/src:fichero-cli/src .venv/bin/python -m fichero_cli engine start --port 8765
The default bind host is 127.0.0.1. If you set it explicitly, keep it
loopback-only:
export FICHERO_BIND_HOST=127.0.0.1
PYTHONPATH=fichero-server/src:fichero-cli/src .venv/bin/python -m fichero_cli engine start --port 8765
Current bind behavior is:
- unset
FICHERO_BIND_HOST: binds127.0.0.1 127.0.0.1,localhost, or::1: allowed0.0.0.0or::: refused- any other non-loopback host: refused unless
FICHERO_ALLOW_NON_LOOPBACK_BIND=I_UNDERSTAND_SHARED_SECRET_RISKis set
That escape hatch is for owner-debugging only. It emits a runtime warning because the shared-secret bootstrap token is not an internet-facing auth boundary. It is not the supported remote-access path.
Publish to the tailnet
Install and sign in to Tailscale on the engine machine and on each client device. Then run this from the Fichero repository on the engine machine:
fichero-server/scripts/start_tailscale_backend.sh --fast
The launcher obtains a Tailscale certificate, keeps the engine on loopback TLS,
starts tailscale serve as https+insecure://127.0.0.1:8765, and advertises the
Tailscale certificate pin to paired clients. Use tailscale serve status to
inspect the generated tailnet URL. The engine itself still only sees a loopback
service.
Create a one-time iPhone/iPad manual pairing link by passing the library path on the Mac explicitly:
scripts/create_tailscale_pairing_link.sh \
"$HOME/Documents/My Library.fichero"
Paste the emitted fichero://pair?... link into Connect → Manual link before
it expires. The link contains a short-lived pairing code and certificate pin; do
not publish it. It is deliberately scoped to the supplied library rather than
guessing from the registry.
Do not run:
tailscale funnel 8765
Do not change the engine start command to:
--host 0.0.0.0
Both patterns create the wrong security boundary for Fichero.
Token handling
The engine writes its API token on the engine host:
"$HOME/Library/Application Support/Fichero/.api-key"
The token authorizes API access once a request has reached the engine. Treat it like a password:
- Do not paste it into shared shell history, tickets, chat, or documentation.
- Do not commit it to a repository.
- Copy it only to devices that should be able to call the engine.
- Rotate it by stopping the engine, deleting the token file on the engine host, and restarting the engine.
The health endpoint is unauthenticated, but library operations require:
Authorization: Bearer <token>
CLI or MCP clients pointed at the Tailscale URL need a paired device credential, not the Mac’s bootstrap token. The iPhone/iPad app receives that credential by redeeming the one-time pairing link above.
export FICHERO_API_URL=https://<engine-name>.<tailnet-name>.ts.net
export FICHERO_API_KEY=<remote-token>
export FICHERO_LIBRARY_PATH=/path/on/engine/Library.fichero
PYTHONPATH=fichero-server/src:fichero-cli/src .venv/bin/python -m fichero_cli --json health
The library path is resolved on the engine machine. A path from the client device is not valid unless the same path also exists on the engine host.
Validation
From a device on the tailnet:
curl -s https://<engine-name>.<tailnet-name>.ts.net/api/health
Expected properties:
- no public DNS or public internet exposure is required
- no engine process is bound to
0.0.0.0,::, a LAN address, or a Tailscale address - protected endpoints return
401until the remote token is supplied - user and object authorization still comes from Fichero, not Tailscale