Private web access
How the web app is served, how new browsers and phones connect without copying a key, and how to reach your agent from other devices without making it public.
Your Oktapod serves its own web app (the Control UI) through its gateway. It is private by design: there is no public Oktapod site that holds your agent. For what the app does, see Ways to use Oktapod.
Where it listens
By default the web app is only reachable from the computer that runs Oktapod:
http://127.0.0.1:8788/
Print the address and key, or open a browser that is unlocked once:
oktapod ui
oktapod ui --open
--open passes the key to the browser in the part of the address after #,
which never reaches the server. The page saves the key for that browser and
removes it from the address bar and history. The key is briefly visible to
other processes on the same computer while the browser opens; on a shared
computer, use plain oktapod ui and paste the key instead.
Connect another browser or phone
Open your Oktapod web address on the new device. The page checks whether it already has access. Your first personal device connects automatically; later devices need your approval once.
If the page is waiting, it names the phone or computer that can approve it. For example, it might say Approve on Edi's Mac Studio, with a note that this is the computer running Oktapod. Open Oktapod there and select Approve on the request at the top of the page. The waiting browser connects automatically.
Without automatic device recognition, choose Ask for approval. It shows a short code. Open Oktapod on a connected device, enter that code in the connection request at the top of the page, and select Approve. You can also enter it under Settings → Devices → Approve a browser.
Already have a code? Choose I have a pairing code. Neither method copies the master key.
Pairing code. On a device that's already connected, open
Settings → Devices → Connect a phone or browser. It shows a QR code and an
eight-letter code such as K7QM-4XPA. Scan the QR code with a phone camera,
or type the code into the new browser or into the Oktapod app under
Use a pairing code. The QR code opens the web app in the phone's browser
and connects it. Choose Oktapod app above the QR code to make it open the
Oktapod iPhone or Android app instead, with the address and code filled in;
you still tap Connect.
Ask for approval. The new browser shows a six-letter code such as
H4N-7KD. Enter it only when you can see it on the device you want to connect.
The new browser connects on its own within a few seconds of approval.
Codes last 10 minutes and work once. Oktapod stores only a hash of each code and limits how many guesses one network can make.
Every device connected this way gets its own access, listed under Settings → Devices with when it was added and last used. Remove cuts off that device alone at once: its next request is refused, and a reply that was already streaming to it finishes and then stops. The other devices keep working.
The access key
The master access key still works, and is under Use an access key instead on the connect card. Use it when no other device is connected yet:
- run
oktapod uiand paste the key into the unlock card, or - copy it from Settings → Advanced → Access → Copy private access key in a browser that is already unlocked.
If a page opens but says the browser is locked, the address works and the browser has no valid access. Use a pairing code, ask for approval, or paste a current key.
Reach it from your phone and other computers
Use Tailscale, a private network between your own devices. Oktapod stays on loopback, and Tailscale Serve publishes the web app to your devices over HTTPS:
oktapod onboard --control-ui tailscale
This needs the tailscale command on the Oktapod computer. Without it,
onboarding skips this step instead of writing a broken setting. On first run,
QuickStart already chooses Tailscale when it is running with a tailnet name,
and local-only access otherwise.
With Tailscale:
- each of your devices is approved once, then signs in with your Tailscale identity by itself, so there is no key to paste;
- HTTPS lets you install the web app on a phone's home screen;
- nothing is exposed to the public internet.
Approve each device once. Tailscale proves a device is yours; Oktapod still asks you to approve it the first time, so a laptop you once signed into, a server or VM on your Tailscale, or a lost phone cannot reach your agent just because it is on your Tailscale. A new device names where to approve it. Open Oktapod on that connected device and the request appears at the top of the page with Approve and Deny. It also appears in Settings → Devices. Background notifications are optional: an open page shows the request even when notifications are off. The waiting device then signs in by itself, and from then on every time. Approved devices are listed as Tailscale devices, and Remove ends that device's access on its next request.
Declined by mistake? Open Settings → Devices → Declined recently on a connected device and select Approve. The waiting page connects automatically. Declined devices stay quiet for seven days, so they do not keep asking.
When nothing is connected yet. While no device is approved at all (a fresh install, or right after updating to per-device approval), the first personal device of yours that opens Oktapod (a Mac, iPhone, iPad, Android phone or Windows PC) is approved automatically. Servers and VMs are never approved that way. From then on, approve new devices from one you already use.
Your first device, even over SSH. On the Oktapod computer, locally or over SSH, run:
oktapod access pair
It prints a one-time code, a link and a QR code for the new phone or computer.
Anyone who can run commands on the Oktapod computer owns it, so this always
works, even when nothing else is connected. The same command family approves a
waiting device (oktapod access approve <code> for a browser showing a code,
or oktapod access approve tsn:<id> for a waiting Tailscale device), lists devices and waiting requests
(oktapod access list), and removes one (oktapod access remove <id>).
The list also shows recently declined devices so you can approve one after a
mistake.
Only you get in this way. Oktapod admits the Tailscale login that owns the Oktapod computer. A computer shared into your tailnet from someone else's account is not let in. When the Oktapod computer is a tagged server rather than a personal device, every login on the tailnet is admitted; to narrow that, or to let a family member in, list the allowed logins:
oktapod config set gateway.tailscale_allowed_logins --value "you@example.com,partner@example.com"
oktapod restart
Tailscale Serve normally publishes on port 443. If another app already uses
443 on that computer, Oktapod takes the first free port of 8443, 9443, 10443
and 11443 instead and prints the address, for example
https://studio.your-tailnet.ts.net:8443/. To choose the port yourself, set
gateway.tailscale_https_port. The route stays in place across restarts so
phones keep working. With gateway.tailscale_reset_on_exit on, stopping
Oktapod removes only its own route; other apps' routes are left alone.
To switch back to local-only access:
oktapod onboard --control-ui local
Behind your own reverse proxy
If you already run a reverse proxy, you can serve the app under a path such as
/agent/:
oktapod config set gateway.control_ui_base_path --value /agent
oktapod restart
Keep access-key authentication on before exposing Oktapod beyond loopback or Tailscale. Oktapod refuses to listen on a non-local address without client credentials configured. Serve it over HTTPS only: over plain HTTP, the access key and each device's own key cross the network readable by anyone on the path.
Tailscale sign-in never applies to your own proxy. Oktapod trusts Tailscale's
identity only while a Tailscale Serve route points at it, and checks the
address your proxy records rather than one the visitor supplies, so a visitor
cannot pose as you by sending Tailscale headers. Still, do not point a public
proxy at the same Oktapod port that a Tailscale Serve route uses. If you must,
turn Tailscale sign-in off with gateway.tailscale_allow_auth: false and use
pairing codes instead.
On a public address, anyone can reach the pairing and approval screens. That is safe: a pairing code is 8 characters, lasts 10 minutes, works once, and Oktapod allows only a few guesses per network and 200 per 10 minutes overall. An approval request does nothing until you type its code on a connected device, so approve only a code you can see on the device in front of you.
When a request comes from the internet (not this computer, a home network or Tailscale), Oktapod also protects you without any setting:
- HTTPS only. A plain-HTTP request from a public address is refused before
any key is looked at, so a key never crosses the internet readable. Terminate
HTTPS at your proxy and have it send
X-Forwarded-Proto: https. - No short keys from outside. An access key shorter than 24 characters
works from nearby only. Keys Oktapod generates are long enough; if you chose
your own, replace it (for example with
openssl rand -hex 24) or connect devices with pairing codes. - Real addresses. Oktapod believes
X-Forwarded-Foronly when a proxy on the same computer sent it, and then only the address that proxy added. Have your proxy append the client address (nginx:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;, Caddy does it by default) so limits on key guessing apply to each visitor. A proxy that forwards without naming the visitor is treated as the internet, so every check still applies; visitors behind it just share one guessing limit. - Setup and metrics stay local. The first-run setup page and metrics only answer requests from this computer that no proxy forwarded.
Settings
| Setting | Environment variable | Purpose |
|---|---|---|
gateway.control_ui_enabled | GATEWAY_CONTROL_UI_ENABLED | Turn the web app on or off. |
gateway.control_ui_base_path | GATEWAY_CONTROL_UI_BASE_PATH | Serve under a path instead of the root. |
gateway.control_ui_root | GATEWAY_CONTROL_UI_ROOT | Use a different app bundle folder. |
gateway.tailscale_mode | serve publishes through Tailscale. | |
gateway.tailscale_allow_auth | GATEWAY_TAILSCALE_ALLOW_AUTH | Tailscale sign-in. On for serve, off for funnel. |
gateway.tailscale_allowed_logins | GATEWAY_TAILSCALE_ALLOWED_LOGINS | Comma-separated logins allowed to sign in. Empty means the owner only. |
gateway.tailscale_https_port | GATEWAY_TAILSCALE_HTTPS_PORT | Serve port. Empty picks 443, or a free fallback port. |
Memory search
Settings → Advanced → Memory search controls how Oktapod finds related memories. There are four options:
- Small and fast (the default). Runs on this computer, works best for English, and uses about 200 MB of memory.
- Multilingual. Runs on this computer and handles several languages better, but uses about 800 MB of memory.
- Online service. Uses OpenAI, or another service with an OpenAI-compatible embeddings address, with your API key. It uses almost no memory on this computer, but your memories are sent to that service to be indexed.
- Off. Finds memories by matching words only.
To switch, choose an option, select Apply and rebuild, and type
REBUILD. Oktapod checks the new option first. If the model cannot be
prepared or the service rejects the key, nothing changes. Once the check
passes, Oktapod restarts once and rebuilds its search index from your saved
memories in the background. Your memories, chats and notes are kept; only the
search index is rebuilt. Until the rebuild finishes, search also matches by
words, and the card shows progress.
If the page is blank or returns 503
A 503 means the app bundle is missing or incomplete. Oktapod refuses to serve
a partial bundle rather than showing a blank page, and its readiness check
fails until the bundle is whole. Packaged installs include the bundle; run
oktapod checkup and, if needed, reinstall or update. On a source checkout,
oktapod onboard and oktapod setup rebuild it when it is missing.