Repository navigation
fix(bundles): generate the admin passwords Crow can choose, and save them to the keychain - #504
Merged
Merged
Conversation
kh0pper
force-pushed
the
fix/bundle-generated-secrets
branch
from
October 16, 2026 21:29
52a55ec to
f8308d5
Compare
…them to the keychain A one-click (collection) install left these blank, so the install failed or the app had no usable login: - miniflux MINIFLUX_ADMIN_PASSWORD and maker-lab-advanced MLA_ADMIN_PASSWORD are generated, saved to the keychain with their usernames, and seed the app's admin on first start. MLA_ADMIN_USER defaults to admin (the compose no longer hard-fails on it). - lemmy's first-admin password was a literal in the public compose file. It is a generated LEMMY_ADMIN_PASSWORD now, and every secret reaches the entrypoint through the environment, so the script text in PID 1's command line holds names, not values. The read-only ./config mount the entrypoint wrote into is gone. - actual-budget: a generated ACTUAL_SERVER_PASSWORD for the server Crow installs, set right after install by a postInstall hook (Actual's first-run bootstrap is first-caller-wins) and as a fallback on first connect. It is only ever sent to a loopback URL. ACTUAL_PASSWORD stays typed, for an Actual server you run elsewhere. - The installer puts generated values a manifest lists in server.envKeys into this instance's MCP entry (mcp-addons.json, mode 600), never into the gateway .env that co-hosted instances share. campaigns and tax encryption keys are not generated yet: that waits for the recovery-key decision (tests/bundle-form-friction.test.js EXEMPT). Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…er never calls sudo on the test host Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
… explains a kept password - Lemmy is left out of this PR: its entrypoint execs a binary the pinned image doesn't have, so it can't start today; generating an admin password it never creates would put a wrong entry in the keychain. Follow-up. - Actual: when the server already has a password (kept data from an earlier install), the hook checks whether the generated one opens it and, if not, says to type the old one into ACTUAL_PASSWORD; the MCP login error says the same. The hook tolerates a missing .env and stays within its timeout. - A stored hash (store_as) never reaches an MCP entry. - maker-lab-advanced's panel says log in (the admin exists from first boot). - Actual docs (en/es) and skill describe the generated password. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
The first-admin password was a literal in the public compose file. It is a
generated LEMMY_ADMIN_PASSWORD now, saved to the keychain (username
admin_pending), and every Lemmy secret reaches the entrypoint through the
environment ($${...}), so the script text in PID 1's command line holds
names, not values. The bundle's entrypoint still runs a path the pinned image
doesn't have (a known issue in its notes; the fix needs a live test).
Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
… login falls back to it A typed ACTUAL_PASSWORD can be stale (kept in the gateway .env from an older install). It is never used to bootstrap a new server any more, and on a loopback server a failed typed login retries with the generated password, in the MCP server and the panel. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
kh0pper
force-pushed
the
fix/bundle-generated-secrets
branch
from
October 16, 2026 21:56
f8308d5 to
d467777
Compare
…ll (stacked on #504) (#505) * fix(bundles): no placeholder value from .env.example reaches an install - servers/gateway/bundle-env-placeholders.js recognises example values (/path/to/, youruser, your-, *.example.com, change-me, replace-me, <...>, xxx, placeholder) and, by SHA-256, example secrets once published here. - The installer leaves example values out when it builds .env from .env.example: such a line becomes a comment naming only the key. - env_vars[].default_in_crow_home: a blank or example path becomes a folder inside this instance's CROW_HOME, created by the gateway (never by Docker as root), and the key no longer blocks install. jellyfin, navidrome, audiobookshelf and kavita start with an empty library there. - 'Finish setup' lists a required key whose value is an example, so existing installs are flagged without rewriting their .env. - tax never encrypts with an example key (decrypt still reads data saved with one); its .env.example no longer carries a key. - .env.example files no longer carry example domains, paths or secrets as live values (kept as commented examples). plex follows once the install-guard PR, which also bumps it, lands. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * docs(bundles): jellyfin and audiobookshelf start with an empty library in Crow's folder Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(bundles): placeholder fixes after review — tax key change keeps old returns readable; domains must be real - tax refuses only the published example key (by hash), never a passphrase for its shape. CROW_TAX_ENCRYPTION_KEY_PREVIOUS opens returns saved with an old passphrase; saving re-encrypts them with the current one. - gotosocial, writefreely and matrix-dendrite fail to start without a real domain (${X:?}) instead of booting with a blank one. - kavita and audiobookshelf keep their named-volume fallback (only the example paths are left out); jellyfin/navidrome's CROW_HOME paths are never copied to the shared gateway .env (propagate: false). Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(bundles): placeholder review fixes — tax key change keeps the old key; reinstall keeps the library path - env_vars[].previous_key: changing tax's encryption key in Configure keeps the old one in CROW_TAX_ENCRYPTION_KEY_PREVIOUS (when that slot is empty), so returns saved with it stay readable. The contract checks the name. - Uninstall keeps default_in_crow_home paths in the retained copy and a reinstall reuses them, so kept Jellyfin/Navidrome data never gets an empty library mounted over it. - GTS_ACCOUNT_DOMAIN is optional (compose falls back to GTS_HOST); the domain fields say they are fixed at first start. - The scrub also handles KEY = value lines; navidrome docs (en/es). Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(tax): key changes never strand data; the tax server reads its key from its own MCP entry - Configure reads the outgoing key where the tax server really runs with it (its mcp-addons entry, then the gateway env, then the bundle .env) and keeps it in CROW_TAX_ENCRYPTION_KEY_PREVIOUS. A second change while that slot still holds a different older key is refused (409), so no key in use is ever overwritten. - At start the tax server re-encrypts every return that only the previous key opens, so the slot can then be cleared. - tax's server.envKeys list both keys, so a typed key reaches the server. - A kept library path follows the latest install; log wording. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * test(tax): the start-time re-encryption to the current key, as a tested function Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(tax): simpler key change — the user keeps the old key in _PREVIOUS; re-encryption never overwrites a newer save The Configure-side copy of the outgoing key (and its 409) is removed: it could only be undone by hand-editing mcp-addons.json. A key change now means: set the new key and put the old one in CROW_TAX_ENCRYPTION_KEY_PREVIOUS (the field and the refusal message say so). Returns move to the new key when the tax server starts or a return is saved; the start-time move is a compare-and-swap, so a save that lands meanwhile wins. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(tax): a key change keeps the outgoing key in _PREVIOUS when that slot is empty Configure copies the outgoing CROW_TAX_ENCRYPTION_KEY (read where the tax server runs with it) into CROW_TAX_ENCRYPTION_KEY_PREVIOUS when the request leaves that slot alone and it is empty or holds the new key. A slot holding another key is never overwritten; the response warns, naming keys only. A typed previous key always wins. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(tax): the previous-key slot always takes the key just replaced; Configure shows server warnings The tax server moves every return to the current key when it starts, so the key that matters after a change is always the one just replaced. Keeping an older one instead stranded everything on the current key at the next change. The Configure dialog now shows the response's warnings in the warning colour, long enough to read. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp * fix(plex): no example media path from .env.example (now that the install-guard PR is underneath); tax error text Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Queue item 30, fix B (one-click audit). When you install a collection, every member gets an empty env and no keychain request. These bundles needed a password typed by hand, so they were left without a usable admin, or their compose hard-failed.
What changes
MINIFLUX_ADMIN_PASSWORD) and maker-lab-advanced (MLA_ADMIN_PASSWORD): Crow generates the password (generate: "secret") and saves it to the Crow keychain with the matching username, through the existingkeychain: truepath that minio, browser and vaultwarden already use. The app creates its admin from it on first start.MLA_ADMIN_USERdefaults toadmin, and the compose no longer hard-fails on it.ACTUAL_SERVER_PASSWORD, generated and saved to the keychain, for the Actual server Crow installs.ACTUAL_PASSWORD.ACTUAL_PASSWORDstays a typed field, for an Actual server you run elsewhere.LEMMY_ADMIN_PASSWORDsaved to the keychain.$${…}), so the script text in PID 1's command line holds variable names, not values.servers/gateway/routes/bundles.js): generated values that a manifest lists inserver.envKeysgo into this instance'smcp-addons.jsonentry (mode 600), and never into the gateway.envthat co-hosted instances share. Stored hashes (store_as) are filtered out..mcp.jsonfiles (both mode 600).Deferred
EXEMPTintests/bundle-form-friction.test.js). Losing a generated key loses the data.Existing installs: nothing rewrites a live
.env. A reinstall reuses the values already there, and keys are never regenerated.Tests:
tests/bundle-generated-secrets.test.js: collection-style install, the keychain entry, the MCP entry, reinstall reuse, Actual bootstrap and hook against a fake server, no literal passwords in tracked compose files.bundle-form-friction,bundles-install-hardening.Version bumps: miniflux 1.0.3, maker-lab-advanced 0.1.2, actual-budget 1.0.1, lemmy 1.0.3. Registry and skill ledger are rebuilt.
Deploy: gateway code. Restart
crow-gateway, plus r4's gateway on the same tree, for new installs to use it.https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp