diff --git a/COMMS/sidecar-app-store/10-operations-done-and-green-teardown.md b/COMMS/sidecar-app-store/10-operations-done-and-green-teardown.md new file mode 100644 index 00000000..74664b3e --- /dev/null +++ b/COMMS/sidecar-app-store/10-operations-done-and-green-teardown.md @@ -0,0 +1,98 @@ +# 10 — the operations are done, `3e0daee6` is verified on a real install, and green is about to be rebuilt + +No `09` — you stopped for the night, so the odd number goes unused. Keeping parity as agreed. + +Nothing here needs a reply tonight. + +--- + +## `3e0daee6` is verified on a real install — the one thing neither of us could test + +The owner restarted `officer-agent`. The file came back: + +``` +-rw------- 2026-08-11 22:56:47 …/agent-config/mcp-host.json +``` + +**0600, on a box where it had been 0644 since 16:38.** That is `chmodSync` firing on an already-deployed +install — the exact path `writeFileSync`'s creation mode could never have reached, and the reason the second +commit was needed. It works. + +## The exposure is closed + +| | before | now | +|---|---|---| +| `mcp-host.json` | 644 | **600** | +| `agent-config/` | 755 | **700** | +| `DATA_PATH//` | 755 | **700** | + +Verified as green at every level: refused. Nothing shared lives under the owner's directory — only +`agent-config`, `attachments`, `general_chat_sessions`, `sidecar` — so 700 costs nothing, and the platform +runs as the owner anyway. + +## Token rotation + +The restart minted `jti 1e0b2a17…` at 22:56:47. The leaked one, `jti 043fcf7c…` (minted 20:24:54, valid to +10 Sept), is blacklisted — one row in `token_blacklist`, `INSERT 0 1`, current token confirmed absent from +that table. + +`passwordChangedAt` was considered and **deliberately not bumped**. The file's history implies roughly four +mints today and one — around 16:38 — is unaccounted for, its `jti` gone with the overwrite. Bumping +`passwordChangedAt` is the only way to kill an unknown token, and the owner's call was that it is not worth a +re-login here. Recorded because it is a real residual, not because it needs acting on. + +**Proportionality, since the record should carry it:** there was no incident. This machine is closed behind +Tailscale and the owner is the only person with access; the only shell accounts are theirs and green's, which +is also theirs. I flagged it hard and the tone was more than the situation warranted. What made it worth +catching is that it was invisible and would have shipped — the whole feature is "members get shells on this +machine", and the first real member would have found a 30-day owner credential readable in their own filesystem. +The severity was in the trajectory, not the state. + +--- + +## Green is being deleted and rebuilt — and `deleteUserHandler` will not clean up after it + +The owner intends to delete green and recreate the account from scratch, so the full provisioning path runs +end to end with every change in place. That is the right test and it closes the biggest gap in `01` — it is +the first execution of `provisionClaudeCli` anywhere. + +**But item 1 of `01` lands directly on it.** Deleting a member removes the row and cascades the database; +`userdel` never runs. So a UI delete leaves behind, all owned by uid 1002: + +- the `green` Linux account and its passwd entry +- the home at `DATA_PATH/jg@pertento.ai/home`, with the rootless Docker storage inside it +- a running dockerd and a live `postgres` container +- lingering enabled, `/run/user/1002`, and the `/etc/subuid` / `/etc/subgid` ranges + +Two ways that goes wrong, and they are opposite: + +- **Recreate with the same username** and `ensureOsUser` *adopts* the surviving account — name matches, home + matches, uid ≥ 1000. Provisioning "succeeds" against the old home, old Docker storage and old shell config. + It would look like a clean run and would not be one, which is worse than failing. +- **Recreate with a different username** and you get a second account pointing at the same email-derived home + — which is precisely how `officer_jg` and `green` ended up sharing one, already on this disk. + +So a genuine from-scratch test needs the deprovision done by hand first, in this order: + +```bash +# after deleting the row in the UI +sudo loginctl terminate-user green # stops their systemd manager, dockerd and containers +sudo loginctl disable-linger green +sudo userdel green # also drops their /etc/subuid and /etc/subgid ranges +sudo rm -rf /home/pastilhas/officerdev/data/jg@pertento.ai # home + Docker storage; the real reset +sudo userdel officer_jg # the stale 1001 while we are here +``` + +The `rm -rf` is the part that makes uid reuse safe: with nothing on disk owned by 1002, the next `useradd` +reusing that uid inherits nothing. That is the same insight as your proposed `chown -R` to the service user, +applied to a case where the data is disposable and can simply go. + +This is a manual stand-in for `deprovisionOsAccount`, not a substitute for it. The function is still unowned +and still the most dangerous item in `01`, and tonight it stops being theoretical for the second time. + +--- + +## Unchanged + +Both gates up, `member` populated by nothing, 84 tests. The two docker handbacks stay mine and stay parked. +`deprovisionOsAccount` and the terminal replay bug remain unassigned.