Skip to content

Security

Starfarer runs in people's homes, holds their conversations, and will one day act for them (mail, calendar, their computer). This file is the threat model, what's built, and the checklist every later step has to meet.

Reporting a vulnerability

Until there's a public address: open a private security advisory on the repository, or contact the maintainer directly. Please don't file security problems as public issues.

What we protect

What Why it matters
Conversations and Logs Private by default; adults' are private even from the Captain
Accounts and sessions Whoever holds one can talk as that person and see their history
Children's data Cadets: family-safe, overseen by the Captain, never exposed to others
The hub's controls Engineering, ranks, personas, models; later: mail, calendar, a computer's screen and files
The models and GPU Ollama has no password of its own

Who we defend against

  • Someone else on the home network (a guest's phone, a compromised smart device).
  • A web page in your browser trying to use your signed-in Bridge (cross-site requests, framing).
  • Password guessing, from one device or many.
  • A household member reaching beyond their rank (a Cadet trying Engineering or another person's Logs).
  • Later, with remote access: anyone on the internet. Later, with tools: instructions hidden in web pages, emails and documents (prompt injection).
  • Out of scope: someone with full control of the hub machine itself (they can read the database, as with any local app). Full-disk encryption on the hub is recommended.

The rules the code enforces

  • Permissions are checked in code on every request, never by her prompt. She can't be talked into Engineering, someone else's Logs, or oversight.
  • Ranks (docs/ARCHITECTURE.md): only the Captain promotes or demotes, and only the Captain can read conversations under oversight (Cadets, anyone under 18, Guests). Oversight is read-only and every read is in the audit log. Adults' conversations are private, even from the Captain.
  • Guests exist only at home, through a paired satellite. The Bridge always requires signing in. A guest needs a paired device (a Satellite or a Shuttle, with its own device key) on the home network, with guests allowed on that device (its guest switch in the Shuttlebay: on by default, off for a Starship), and a voice that matches no one. With guests off, a voice it doesn't know isn't answered. Anything arriving by remote access (a proxy, a tunnel, Tailscale) is remote and never gets guest access, whatever address it seems to come from.
  • A Shuttle's screen holds no key. Its page is served by the Satellite program on the device itself, to that device only (127.0.0.1), and shown in a browser profile of its own (nobody's browsing, passwords or sign-ins). The program passes along only the hologram's files and the persona list from Starbase, with the device's key; the page never sees the key, and a device's key can't open anything else. Cards (the weather, a list) go only to a Shuttle in the room where they were asked for out loud.
  • A Starship's hands are its owner's, switched on at the PC itself. A Starship (someone's own PC or laptop) does nothing for the AI until its person turns on "Let the AI use this PC" in its tray menu, on that PC; nothing on Starbase can turn it on, and the Captain can only turn Starship hands off for someone (Clearance), never use anyone else's. Only its owner's requests reach it (from their Bridge, or a voice that's theirs). The same rules as the Starbase PC apply: look-only at once, anything that changes something needs their OK and PIN (the PIN typed in a window of its own that takes the keyboard, or said), the Never list (their home and system folders, a disk), and the audit log. The PC itself shows a notification for every action and runs its own Stop key (Ctrl+Shift+F12), which ends whatever's running there and drops what waits. Its corner window and tray reach the Satellite program only through 127.0.0.1 with a key a web page can't read (local.token, this user only), and only requests addressed to 127.0.0.1 or localhost are answered (no DNS rebinding). A file sent to Ops → Files goes up with a one-time ticket for that request, is kept as it came, and is only ever downloaded (never shown as a page).
  • A file sent to the AI in a chat is its sender's alone. The Bridge's paperclip and a Starship's window send it up in pieces as JSON (like every change to Starbase, so no other site can send one), signed in or with the Starship's own key (for its owner); never a Guest's. It waits a day in that person's own folder for what they say to do with it, and nobody else can use it, the Captain included; the audit log has its kind and size, never its name. What it is comes from what's in it, not its name; only a PDF, a Word document or a text file is read (onto their own reading shelf), only a picture goes to the Vision model (on Starbase, like every model), and only what they ask for goes into their Files. Pictures and reading each have a Clearance line.
  • The Cargobay is each person's own, and only holds media. Big videos, pictures and sound go up in raw pieces that need their own header (X-Cargo-Piece), which no other site can send without the browser asking Starbase first, signed in or with a Starship's own key (for its owner); never a Guest's, and only for people the Cargobay's Clearance line allows. Each piece is written to the disk as it comes, never held whole in memory; the first one shows what it is, and anything that isn't a video, a picture or sound is deleted at once. Nobody else sees a person's Cargobay, the Captain included; the audit log has each file's kind and size, never its name. Each person's space and what the drive always keeps free are the Captain's; where it is (Starbase's own folder, or a folder on a drive in the Starbase PC, never a network share) changes only at home with their PIN. The AI sees the list, never what's in a file, except a picture it's asked to look at (the Vision model, on Starbase); moving a file onto the Starbase PC needs PC hands and their OK and PIN, deleting one their OK and PIN. A file is downloaded, or previewed in their own Cargobay as the picture, video or sound it is (only the kinds a browser shows by itself, with nosniff and the site's own script rules), never as a page.
  • What a device keeps for when Starbase is down is only sound and words. Its wake words and each persona's two lines for "Starbase can't be reached" (WAVs Starbase makes in their voice) are fetched with the device's own key and kept in its settings folder. They hold nothing about anyone, and the device says them only when someone calls it. A Shuttle's offline screen shows Starbase's address on the home network and why it can't be reached, on that device's own screen.
  • A device's log shows how it's doing, not what anyone said. The Captain and First Officer can read the end of a connected device's log in the Shuttlebay (it's asked for over the device's own connection, and each read is in the audit log). The device never writes anyone's words into its log, and leaves out any it finds there from an older version.
  • What the AI says about the ship is read, not made up. On a hello, a systems report or "what's going on today", the AI is given Starbase's real readings (what's connected, each device's mic, speaker and wake words, its version, speech-to-text and the AI's voice, disk space); it isn't told about anyone's personal device but theirs (the Captain and First Officer: all). When something breaks it cuts in only on the Captain's or First Officer's next message; when they look into it, it's given the warnings and errors from that device's own log (no one's words are in it), and that read is in the audit log like one in the Shuttlebay. A question held while it cut in is kept in Starbase's memory only, and expires (Engineering → Starbase → Systems).
  • "One sec" never says who or what. When the AI is busy with someone else's answer, the fixed line someone waiting hears doesn't say who it's busy with or what about; Sensors (Captain and First Officer) shows only the kind of work (an answer, her journal).
  • Uninstalling removes only the Satellite. From the Shuttlebay (Captain or First Officer, audited) a connected device stops and removes its own start-up entry, settings and program folder, and nothing else: only folders its installers made (named for it, with its own Python), never Python, the browser or a copy run from elsewhere. A device can also ask Starbase to forget it, with its own key, when it's uninstalled on the device.
  • Updates come only from the device's own Starbase. They're sent over its paired connection (checked against the certificate it pinned when it was set up), only when the Captain or First Officer presses Update in the Shuttlebay, or fetched with the device's own key when its person asks it to. The device checks the update's fingerprint, tries the new program before installing it, keeps the old one, and goes back to it if the new one can't reach Starbase.
  • A voiceprint says who's talking, not that it's really them (a recording can fool it). Anything that changes something needs a spoken PIN or a tap on a phone (step 5 with Clearance).

What's built (step 1)

Area In place
Passwords scrypt (salted, memory-hard); at least 8 characters; common passwords and passwords that are mostly your own name are refused; same response time whether or not a username exists
Sessions random 256-bit tokens, stored only as hashes; cookie is HttpOnly, SameSite=Strict, Secure under HTTPS; 12 hours, or 30 days renewed while used ("keep me signed in"); changing a password signs out every other device
Devices Ready Room → You → Security lists signed-in devices; sign one out, or all others
Sign-in throttling per account (5 failures / 15 min) and per device address (20 / 15 min); kept in the database, so a restart doesn't reset it
Recovery 8 one-time recovery codes at Welcome aboard (hashed; new set in Ready Room for the Captain and First Officer); the Captain resets anyone else; python -m starfarer reset-password <user> works only on the hub machine
Cross-site protection every change must come from the hub's own page (Origin check) and be JSON; the live connection checks its origin and needs a session
Browser headers Content-Security-Policy (scripts only from the hub, no framing, no plugins), X-Frame-Options DENY, nosniff, no referrer, same-origin isolation, HSTS under HTTPS; API responses are never cached; no API browser (/docs) exposed
Files served only the Bridge's own files, by name
HTTPS automatic as soon as the hub listens beyond this computer: the hub is its own small certificate authority, makes a certificate for its names and home addresses, and renews it; devices trust the CA once (Ready Room → You → Security has the steps)
Audit log sign-ins (and failures), password / PIN / recovery changes, devices signed out, crew added / changed / promoted / removed, settings, personas, model downloads, answered requests, every oversight read (and refusal), blocked cross-site attempts; kept 400 days; Engineering → Crew → Audit log
Warnings the Bridge warns the Captain if Ollama answers on the home network (it has no password) or lives on another machine, and when recovery codes run low
Data one database (data/starfarer.db, SQLite in WAL mode) with versioned upgrades; each person's facts and lines are only ever queried by their own id
Backups Engineering → Starbase → Backups: now or scheduled, to a folder the Captain picks (changing it takes the PIN); only this install's own backup files are ever removed; restore is the Captain's, at home, with the PIN, and saves what's there first. A backup holds everything the data folder does (but the models and logs), so keep it somewhere private; the saved passwords and keys in it open only on this computer
Saved passwords and keys mail passwords, sign-in tokens (Google, Microsoft), the household's Google keys, tool servers' tokens and environment values, Modules' secret settings: scrambled (AES-256-GCM) with a key locked to this computer (core/vault.py). On Windows the key is protected by Windows itself (DPAPI, "this computer" scope): it opens by itself when Starbase starts, with or without anyone signed in to Windows, and after a password reset; on Linux and macOS it's a file outside the data folder (~/.config/starfarer/keys), readable only by the account running Starbase. A copy of the data folder, a backup or the database elsewhere can't open them; moved to another computer, accounts are reconnected and keys pasted again. It doesn't stop something running on the Starbase computer itself (BitLocker covers a stolen drive). If Windows can't lock the key, values stay as they were and the Captain is warned

Outside access (Engineering → Starbase → Domain, 2026-11-12)

  • Closed until the Captain opens it, at home, with their PIN, and only once they have two-step sign-in themselves. While it's closed, anything from outside (forwarded headers, the household's outside address, an internet address, Tailscale) is turned away at the door, before any page or API.
  • Outside is never home. A request is outside if it came through a tunnel or proxy (any forwarded header), was made to the household's outside address (the Host), or came from an address that isn't a home one. Home needs the opposite on every count, so a tunnel on Starbase's own PC (127.0.0.1) still counts as outside.
  • Signing in from outside takes two steps: the password, then a code from an authenticator app (TOTP, RFC 6238; each code works once; a recovery code works in its place). Someone without two-step sign-in can't sign in from outside at all. Its key is sealed with the vault. "Keep me signed in" from outside lasts a week; a session started at home is refused from outside; turning two-step off signs out every outside session.
  • Home only: Guests, pairing devices, the AI's hands on the Starbase PC (and on any computer but the one you're talking on), moving Cargobay files onto the Starbase PC, making the Captain, using a recovery code to reset a password, setting up or turning off two-step sign-in, changing outside access, changing where backups go and restoring a backup.
  • Devices from outside (2026-11-13) only when they're let out. A paired device connects from outside only when the Captain or First Officer let it out (Shuttlebay → Edit; their PIN, at home; off to start with) and outside access is on. Starbase gives such a device the household's outside address over its connection at home (an https address only; nothing is typed on the device), and the device tries home first, then that address, and goes back to home as soon as Starbase answers there. From outside it checks the address's own certificate against the computer's trusted ones (and Starbase's own), with the name matched. Its device key is its only credential, so each outside address gets five wrong keys in 15 minutes, then waits; every connection from outside is in the audit log, and the Shuttlebay marks it Away. While a device is away Guests aren't answered on it.
  • From outside, the AI's hands reach only the computer you're on (2026-11-13, the Captain: "just not control my remote server/computer. in the way of running code on it remotely"). A request is from outside when it comes through the household's address (the Bridge, its mic) or from a device that's away. Then the only PC the AI may use is the Starship it's said on (its owner's, its switch on, OK and PIN for changes, a notification there for every action, its Stop key); never the Starbase PC or another Starship. A command waiting for an OK runs only where that's allowed when it's approved. A Starship that's away takes commands only from itself (Starbase refuses any other). Away missions started or scheduled from outside get no PC at all, and a mission (or schedule) waiting to use one can't be approved from outside. Moving Cargobay files onto the Starbase PC is home only.
  • Turning a device back to home only (or outside access off, or unpairing it) stops it at once, and the device forgets the outside address when Starbase turns it away.
  • The visitor's real address is read from X-Forwarded-For only when the request comes from a tunnel or proxy the Captain listed (127.0.0.1 by default); from anywhere else those headers are ignored. Sign-in throttling and the audit log use it.
  • The page's own outside address passes the origin check (for proxies that rename the Host), cookies are Secure and HSTS is sent there.

Checklist before remote access (Tier 2)

  • [x] Remote access through a tunnel (Cloudflare Tunnel, Tailscale Funnel) or a reverse proxy the household runs; Starbase's own port is never forwarded (Domain says so, and checks the address).
  • [x] The tunnel path is marked remote end to end (forwarded headers make a request remote; tested).
  • [x] Two-factor (authenticator app, TOTP) required for remote sign-in; recovery codes cover a lost phone.
  • [ ] Two-factor for the Captain and First Officer everywhere (at home too).
  • [x] Remote sessions shorter, and marked in Signed-in devices.
  • [x] Guests impossible remotely (tested).
  • [x] Node pairing: per-device keys, approved on the Bridge, revocable; never from outside (a device let out connects from outside, but pairs at home).
  • [ ] Throttling on every endpoint that's expensive (chat, model downloads), not just sign-in.

Checklist before publishing (Tier 3)

  • [ ] Independent security review by someone who didn't write the code.
  • [ ] Dependency scanning in CI (pip-audit) and pinned versions for releases; signed release builds.
  • [x] Secrets (mail passwords, Gmail / Outlook tokens, API keys) encrypted at rest, the key locked with the OS (Windows DPAPI), never in plain files or logs. Still to do: the hub's HTTPS keys (data/tls).
  • [ ] Modules: signed, permission manifests shown before install, enforced at run time (step 4).
  • [ ] Tools (step 5): Clearance checked in code; content from web pages, mail and files is data, never instructions; every action logged; "ask first" actions confirmed by the person (PIN / phone for voice).
  • [ ] Privacy: export and delete everything about a person; clear notices for oversight; check children's-data rules for any hosted service.
  • [ ] A public vulnerability-reporting address and a response promise.