the install root is derived, not configured
Seven variables out of .env. DATA_PATH, OFFICER_ITEMS_DIR and HOME_DIR are gone from the code entirely; PUBLIC_URL, PUBLIC_BUILD_ENV, JWT_SECRET and VAULT_STORE_KEY are no longer written by the setup script. data-path.ts now derives OFFICER_ROOT as dirname(process.cwd()), with data/, capabilities/ and dockers/ as fixed names under it. The direction used to run the other way — DATA_PATH from env, then OFFICER_ROOT = dirname(DATA_PATH) in app-store/paths.ts — which meant three environment variables that had to agree with each other and with the tree on disk. Eight files re-read process.env.DATA_PATH independently, each with its own `?? cwd()/data` fallback. They import the one value now, which is what made removing it safe: otherwise each would have derived its own and drifted. Three things this turned up. The cwd pin in ecosystem.profile.cjs was broken. It set `cwd: __dirname` under a comment asserting "__dirname is the repo root — this file sits beside ecosystem.config.cjs", which stopped being true when these files moved into ecosystem-files/. It walks up to the platform's package.json now, which holds wherever the file lives. That was a live bug before this change and a load-bearing one after it, since cwd now decides where the install is. assertInstallLayout joins the other two boot assertions. A wrong cwd does not error — it computes a plausible root somewhere else and writes managed homes and agent runs into it, so the install looks empty and the data looks lost with nothing naming the cause. It throws before serve(), first of the three, because a wrong answer there makes the other two check the wrong files. getOwnerHomeDir captures homedir() once at module load rather than per call. Measured on bun 1.3.10: both os.homedir() and os.userInfo().homedir return $HOME when set rather than reading passwd, and user-instance.ts assigns process.env.HOME on its way to spawning an agent. A lazy read would have returned the owner's home on the first call and a member's afterwards. data-path.ts imports only node builtins, so it is evaluated before any of that runs. JWT_SECRET and VAULT_STORE_KEY leaving .env means an install made by this script does not boot — jwt.ts throws at module load without one. That is the agreed sequencing: they move to the SQLite store (docs/secret-store.md), and writing them here meanwhile would create a second origin for a secret the store then has to be reconciled with. Said plainly in .env.example and in lib/env.sh rather than left to be discovered. Not typechecked: node_modules is empty here and installs are frozen. Every edited file parses under `bun build --no-bundle`; the profile loads and pins the right cwd; assertInstallLayout was exercised from both the repo and /tmp; the setup section was run and writes five variables. Prettier was NOT run — 3.9.6 via bunx is not the pinned resolution and reformatted unrelated unions and line wraps in six files, so those were reverted and the edits re-applied by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-20
@@ -1,22 +1,44 @@
|
||||
# What officer-setup writes. Everything below this block is optional, or is on its way out.
|
||||
PORT=9000
|
||||
JWT_SECRET="<generate with: openssl rand -base64 32>"
|
||||
BROWSER_RELAY_PORT=18792
|
||||
POSTGRES_URL="postgres://postgres:password@localhost:5432/officer"
|
||||
PUBLIC_URL=http://localhost:9000
|
||||
|
||||
# ── Moving to the secret store ─────────────────────────────────────────────────────────────────
|
||||
# Still REQUIRED — jwt.ts throws at module load without JWT_SECRET, and crypto.ts throws without
|
||||
# VAULT_STORE_KEY — but officer-setup no longer writes either. They are moving into the SQLite key
|
||||
# store (docs/secret-store.md), which is designed and not yet built, so an install made by the
|
||||
# current script will not boot until it is. That is deliberate sequencing, not an oversight.
|
||||
JWT_SECRET="<generate with: openssl rand -base64 32>"
|
||||
|
||||
# NOT Vaultwarden's, despite the name and where it used to sit — it is the platform's at-rest key,
|
||||
# encrypting every secret column in Postgres: Headscale admin API keys, app-store service
|
||||
# credentials, Jellyfin tokens, wallet node credentials, and the wallet seed envelope on top of the
|
||||
# owner passphrase that seals it.
|
||||
#
|
||||
# CHANGING IT MAKES ALL OF THAT UNREADABLE AT ONCE, and for the seed that is unrecoverable: the
|
||||
# passphrase opens the inner envelope and this is the outer one.
|
||||
VAULT_STORE_KEY="<generate with: openssl rand -base64 32>"
|
||||
|
||||
# ── Optional ───────────────────────────────────────────────────────────────────────────────────
|
||||
# Where Officer is reached from a browser. Read by origin validation, the task API host check, and
|
||||
# the CalDAV iOS profile builder — which is the only one that hard-requires it, and demands https.
|
||||
# PUBLIC_URL=https://officer.example.com
|
||||
|
||||
# Guards (CORS origin checks, rate limits, password-strength rules) are ON unless this is set to
|
||||
# "dev" or "development". Leave it unset or set it to "production" for a real deployment; only set
|
||||
# it to "dev" on a local machine you trust, since that disables all three.
|
||||
PUBLIC_BUILD_ENV=production
|
||||
# "dev" or "development". Unset is hardened, which is why officer-setup no longer writes it — set it
|
||||
# by hand, on a local machine you trust, to develop. Note that `bun dev` does NOT set it: that script
|
||||
# only loads this file, so `bun dev` against a production .env runs fully hardened.
|
||||
# PUBLIC_BUILD_ENV=dev
|
||||
|
||||
# Origin checking is OFF unless this is explicitly "false" — an inversion of the usual rule, and one
|
||||
# that is only defensible when the tailnet is the perimeter. On a machine with no tailnet, set it to
|
||||
# false. Written explicitly rather than left to the default so the choice is visible.
|
||||
ALLOW_ANY_ORIGIN=true
|
||||
|
||||
DATA_PATH=/path/to/data
|
||||
OFFICER_ITEMS_DIR=/path/to/officer-items
|
||||
HOME_DIR=/home/user
|
||||
BROWSER_RELAY_PORT=18792
|
||||
# DATA_PATH, OFFICER_ITEMS_DIR and HOME_DIR were here until 2026-08-12 and are no longer read.
|
||||
# The install root is derived as the parent of the working directory (src/servers/data-path.ts), so
|
||||
# data/, capabilities/ and dockers/ follow from it; the owner's home comes from the OS. Three values
|
||||
# that had to agree with each other and with the disk became one that cannot disagree.
|
||||
|
||||
# ── Sidecars ────────────────────────────────────────────────────────────────────────────────────
|
||||
# Each sidecar owns its upstream's credentials; the platform API is only a thin auth+forward proxy
|
||||
@@ -35,19 +57,10 @@ BROWSER_RELAY_PORT=18792
|
||||
# daemon URL and its API key live encrypted in `service_connections`; the sidecar injects the key as
|
||||
# X-API-Key on every forwarded request.
|
||||
|
||||
# Vaultwarden (officer-vault).
|
||||
# Vaultwarden (officer-vault). VAULT_STORE_KEY is at the top of this file — it is the platform's
|
||||
# key, not Vaultwarden's, however much the name and its old position here suggested otherwise.
|
||||
VAULTWARDEN_URL=http://127.0.0.1:8222
|
||||
|
||||
# VAULT_STORE_KEY is NOT Vaultwarden's, despite the name and where it sits — it is the platform's
|
||||
# at-rest key, and it encrypts every secret column in Postgres: Headscale admin API keys, app-store
|
||||
# service credentials, Jellyfin tokens, wallet node credentials, and the wallet seed envelope on top
|
||||
# of the owner passphrase that seals it. Any strong secret of 16+ chars works.
|
||||
#
|
||||
# CHANGING IT MAKES ALL OF THAT UNREADABLE AT ONCE, and for the seed that is unrecoverable: the
|
||||
# passphrase opens the inner envelope and this is the outer one. See docs/secret-store.md, which is
|
||||
# the design for moving this key out of here and making rotation a supported operation.
|
||||
VAULT_STORE_KEY="<generate with: openssl rand -base64 32>"
|
||||
|
||||
# Anthropic proxy (officer-anthropic-proxy). Defaults to 5051; it holds the API credential, which
|
||||
# lives in the host env rather than here.
|
||||
# ANTHROPIC_PROXY_PORT=5051
|
||||
|
||||
Reference in New Issue
Block a user