| 01-federation-mesh-hardening |
11 |
security |
| secrets |
| bcrypt |
| fedimint |
| credentials |
| shell |
| rust |
|
| phase |
provides |
| 01-federation-mesh-hardening |
apps/fedimint-gateway/manifest.yml's existing `generated_secrets: fedimint-gateway-hash (kind: bcrypt)` block and container::secrets::ensure_one's Bcrypt arm, which already materialised a per-install credential at 0600 — this plan makes the five paths that bypassed it agree with the manifest |
|
|
| One canonical per-install gateway credential accessor (container::secrets::gateway_bcrypt_hash) plus an idempotent generator (ensure_gateway_credential), used by the daemon and relied on by all four shell paths |
| A detection-only denylist (KNOWN_DEFAULT_GATEWAY_HASHES) that plan 01-16's migration consumes to find and rotate installs still carrying the shipped default |
| Fail-loud semantics on every configure path: no credential means no gateway container, never a shipped default |
|
| fedimint-gateway |
| container-secrets |
| deploy |
| first-boot |
| reconcile |
|
| added |
patterns |
|
|
| Credential-shaped secrets get one canonical accessor in container::secrets that returns Result, plus a denylist check inside that accessor — so refusing a known-compromised value is structurally impossible to bypass, rather than a rule each caller has to remember. |
| Shell install paths stop generating credentials entirely and defer to the daemon's generator; where a script cannot obtain one it skips container creation with a printed reason instead of substituting anything. |
|
|
| created |
modified |
|
|
| core/archipelago/src/container/secrets.rs |
| core/archipelago/src/api/rpc/package/config.rs |
| core/archipelago/src/api/rpc/package/dependencies.rs |
| core/archipelago/src/api/rpc/package/install.rs |
| scripts/first-boot-containers.sh |
| scripts/reconcile-containers.sh |
| scripts/deploy-to-target.sh |
| scripts/deploy-tailscale.sh |
| scripts/container-specs.sh |
|
|
| Credential-less install failure mechanism: `get_app_config` was widened from a bare tuple to `Result<tuple>` and the fedimint-gateway arm propagates with `?`. Chosen over the plan's alternative (log + an argv the install path rejects) because it makes the failure unrepresentable rather than conventional — an install with no credential cannot reach `podman run` at all, and every other arm was mechanically wrapped in `Ok(...)` with no behaviour change. The one call site in install.rs became `.await?`. |
| `configure_fedimint_lnd` now takes the resolved hash as a parameter instead of re-reading the secrets file with its own fallback, so there is exactly one read site and one failure point. |
| `read_secret(name, default)` in config.rs was left intact — 6 other call sites still use its default parameter, so per the plan's explicit branch the gateway was simply routed off it rather than removing the helper. |
| Shell paths generate nothing: first-boot, reconcile and both deploys defer bcrypt generation to the daemon. This removes the htpasswd host dependency entirely (the plan's preferred branch) and keeps bcrypt generation in exactly one implementation. |
| deploy-tailscale.sh's gateway argv was switched from the plaintext `--password` flag to `--bcrypt-password-hash`, matching every other path; it reads the hash on the target rather than shipping the deploy host's copy of it. |
| Legacy `fedimint-gateway-password` files are copied forward to the canonical `fedimint-gateway-hash.pw` name (0600) and never deleted or regenerated, so a node with a working unique credential keeps it. Plan 01-16 owns retirement of the legacy name. |
|
|
| id |
description |
requirement |
verification |
human_judgment |
| D1 |
A fresh install derives a per-install gateway credential; two installs never share one |
FED-07 |
| kind |
ref |
status |
| unit |
core/archipelago/src/container/secrets.rs#gateway_credential_fresh_generation_verifies_and_is_0600, #gateway_credential_is_per_install_not_per_build |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D2 |
No code path configures a gateway with a credential literal carried in this repository; a missing credential fails loudly instead of starting a defaulted gateway |
FED-07 |
| kind |
ref |
status |
| unit |
core/archipelago/src/container/secrets.rs#gateway_credential_missing_is_a_named_error (error names the missing file) |
pass |
|
| kind |
ref |
status |
| other |
grep -rl 't9YjjxkiktrlYvjajB' --include='*.rs' --include='*.sh' --include='*.yml' --include='*.json' . -> exactly 1 hit, core/archipelago/src/container/secrets.rs (the denylist) |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D3 |
The compromised hash exists in exactly one place, as a denylist never used to configure a container |
FED-07 |
| kind |
ref |
status |
| unit |
core/archipelago/src/container/secrets.rs#gateway_credential_rejects_known_default — the accessor returns Err rather than handing the value back |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D4 |
One canonical secret filename across the Rust orchestrator, first-boot, reconcile and both deploy scripts |
FED-07 |
| kind |
ref |
status |
| other |
GATEWAY_HASH_SECRET_NAME const in secrets.rs matches manifest generated_secrets; all four scripts read/write fedimint-gateway-hash{,.pw} |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D5 |
A first boot on a host without htpasswd still yields a unique credential rather than a shipped one (the ISO path that put the default on real nodes) |
FED-07 |
| kind |
ref |
status |
| other |
scripts/first-boot-containers.sh — htpasswd removed entirely (grep -v '^\s*#' | grep -c htpasswd == 0); gateway creation is skipped with a logged reason until the daemon generates the credential |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D6 |
Generating the credential twice is idempotent — a reconcile tick never rotates a working gateway out from under itself |
FED-07 |
| kind |
ref |
status |
| unit |
core/archipelago/src/container/secrets.rs#gateway_credential_is_idempotent |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D7 |
All five changed scripts remain syntactically valid |
FED-07 |
| kind |
ref |
status |
| other |
bash -n clean on first-boot-containers.sh, reconcile-containers.sh, deploy-to-target.sh, deploy-tailscale.sh, container-specs.sh |
pass |
|
|
false |
|
|
135min |
2026-07-31 |
complete |