2e8ec845c8c7fd0f8923145c144fc7349e271420
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2e8ec845c8 |
step 1/4: the permission engine is called permissions, not capabilities
The word meant four different things in this repo, not the three the offscale
doc records:
1. the permission registry → RENAMED here
2. $OFFICER_ROOT/capabilities/ items → kept; this is what capabilities are
3. sidecar routing keys → step 3, becoming `handles`
4. Lightning wallet features → kept; a domain term, and on the wire
to the mobile apps
The fourth was not in the doc and a global find-and-replace would have broken
the mobile wallet, which reads `{ kind, capabilities: Capability[] }` from the
wallet sidecar. So this renamed against an explicit file allowlist rather than
by sweeping the tree, and `CapabilityPage.tsx` — the UI for the item store, and
correctly named already — was left alone.
Moved: servers/capabilities/ → servers/permissions/, capability-gate.ts →
permission-gate.ts, users/capabilities-routes.ts → permissions-routes.ts,
hooks/useCapabilities.ts → usePermissions.ts. Identifiers follow.
Three breaks the typechecker could not see, all found by exercising it live.
The route paths moved with the prose sweep, so the server served
/user/permissions while the frontend still called /user/capabilities. A 404 on
every page load, and tsgo clean throughout.
The response FIELD moved too. `client.get<SelfPermissions>()` is an unchecked
cast, so `data.capabilities` became `undefined` at runtime with no compile
error — `can()` would have answered "no" to everything and the dock would have
emptied itself.
And the grants list was passed straight out of the database, so it arrived as
`{ role, capability, level }` while the screen read `grant.permission`. Every
role would have rendered as holding nothing. It is now mapped in the route:
the wire says `permission`, the column still says `capability`, and step 2
therefore changes nothing any client can see.
The stale react-query keys were the quiet one: two files still invalidated
['self-capabilities'] after the hook moved to ['self-permissions'], so
installing a plugin would have silently stopped refreshing the dock.
The database is untouched — `role_capabilities` and its `capability` column are
step 2, and the two call sites that cross that boundary say so in a comment.
Round-tripped the 9 live grants through the admin endpoint to prove the PUT
contract survived: 9 before, 9 after, Member's three intact.
Also reverted prettier churn on five landing-page files that a broad --write
picked up. Second time today; the lesson is not sticking.
tsgo clean. 797 tests, 787 pass, same 7 pre-existing failures — two of which
now read "path → permission" rather than "path → capability".
|
||
|
|
3f071c0b24 |
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> |
||
|
|
a64d5610e6 |
officer-setup section 5: Postgres, and only Postgres
The original offered five containers. Of those:
Postgres the only database Officer has — account, passkeys, settings,
dashboards, email accounts, the queue. Required.
Redis not referenced anywhere in the platform. No import of the client,
no environment variable, no mention; the only "redis" string in
src/ is the word "rediscover" in a comment. Dropped. (It is still
in package.json and comes out in the dependency pass.)
SearXNG zero references anywhere. Dropped. If it ever arrives it brings
its own compose file and its own Redis with it.
Mailhog a development convenience, offered separately rather than here.
Nginx PM a deployment choice — Caddy, Traefik, nginx or the tailnet — and
not something a setup script should pick.
Provisioned into $OFFICER_ROOT/dockers/postgres/, the same convention the app
store uses: one directory per service, the compose file in it, relative bind
mounts so the data sits beside the compose file.
Bound to 127.0.0.1, deliberately and with the reason in the compose file itself.
Docker publishes ports by writing iptables rules underneath ufw, so "5432:5432"
is reachable from the internet whatever the firewall reports — the same mechanism
the machine-setup firewall section exists to close. The platform runs on this
machine, so loopback is all it needs.
The password lives in a 0600 .env beside the compose file rather than inside it,
so the compose file can be read or copied without carrying a credential. A second
run reuses it rather than minting a new one, which would leave the container and
the URL disagreeing.
Readiness is waited for rather than assumed: Postgres initialises its data
directory on first start, and db:push against a database that is still starting
fails in a way that reads as a schema problem.
Choosing an existing database checks the URL but does not insist on it — the URL
may be right and the database not yet started, and refusing to continue over that
would be worse than saying so.
Also carries the whitespace fix for the comment removed in the previous commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
911b79b6a2 |
drop a comment describing a machine that no longer exists
paths.ts carried a parenthetical explaining that the development machine had the layout inverted — the project inside ~/dockers/officer.dev/, so the root derived to officer.dev and the app store's directory came out as a dockers inside a dockers. That machine is gone. The project sits at ~/officerdev/platform, which is the clean shape the comment said new installs would get. Anyone reading it now goes looking for a directory that is not there and comes away unsure whether the derivation can be trusted. The rule above it is unchanged and is the whole contract: data/ is a direct child of the root, and OFFICER_ROOT is dirname(DATA_PATH). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
654cb10711 |
app store: put provisioned containers under the officer root, not the user's
The install layout a machine should have, seasoned owner or not:
~/officerdev/
platform/ the app
data/ DATA_PATH
dockers/ services the app store provisioned
capabilities/ the file-based item store
One root, everything under it. OFFICER_ROOT derives from DATA_PATH rather than being a second variable
that has to agree with the first.
Deliberately not `~/dockers`, where a seasoned user already keeps their own estate — 47 services on this
machine. That separation buys two things. Containers the app store created are distinguishable from the
user's own structurally, rather than by a naming convention we would have to enforce and they could
break. And we never reason about someone else's compose files: the store does not scan, adopt or modify
anything outside its own directory.
That also simplifies "I already have one of these" — it is answered by the user giving a URL, never by
us finding a directory and guessing whose it is. An earlier draft had the installer adopting existing
directories, which meant reading, and potentially writing over, services Officer did not create.
This development machine predates the convention and derives an ugly-but-correct path, since the project
sits inside ~/dockers/officer.dev. Still isolated, still one root. New installs get the clean shape.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|