Move SLIM identity beyond the shared-secret PSK (JWT / JWKS / SPIRE)
Type: enhancement / hardening — hard prerequisite before anything hosted or multi-user.
Current state
SLIM app identity today is a deterministic shared-secret PSK: a per-channel secret is HMAC-SHA256(master_secret, "workspace/room"), derived independently by every host so no key-exchange round-trip is needed. Passed to the fabric via service.create_app_with_secret(name, secret).
- Master secret:
MYCELIUM_SLIM_MASTER_SECRET, else a public dev literal committed in-repo (mycelium-dev-shared-secret-v1-do-not-use-in-prod) — anyone with the repo can derive it, so it authenticates nothing on its own.
MYCELIUM_SLIM_REQUIRE_SECRET=1 fails closed on a host that forgot to set a real secret.
- Source:
fastapi-backend/app/services/slim_client.py (~L61-75, mint_shared_secret/resolve_master_secret) and the CLI mirror mycelium-cli/src/mycelium/slim/naming.py.
- CLAUDE.md already flags this: "real identity (JWT/SPIRE) is a hard prerequisite before anything hosted / multi-user."
Gaps this creates
- No per-agent identity. The secret is scoped to
workspace/room — the agent segment is ignored — so every member of a room is cryptographically indistinguishable.
- No revocation. Removing an agent's access means rotating the room/master secret for everyone.
- Shared-secret parity trap across hosts (documented in
docs/cross-machine.md): every host must set the same private master secret out of band.
What the fabric already supports (from the audit)
slim-bindings exposes a full identity stack we don't touch — verified in the upstream examples (_slim-research/slim-bindings/python/examples/common.py):
- JWT + JWKS —
IdentityProviderConfig.JWT / IdentityVerifierConfig.JWT, ClientJwtAuth, JwtKeyConfig (JwtAlgorithm.RS256, JwtKeyFormat.PEM|JWKS, JwtKeyData), audience/issuer/subject/duration. Identity = the sub claim; per-agent identity falls out naturally.
- SPIRE / SPIFFE —
IdentityProviderConfig.SPIRE / .SPIRE verifier, SpireConfig(socket_path, target_spiffe_id, jwt_audiences, trust_domains). Consumes JWT-SVIDs from the SPIRE Workload API: zero-secret bootstrapping, automatic rotation, workload attestation. Upstream's recommended production setup.
- App creation switches from
create_app_with_secret(...) to service.create_app(name, provider_config, verifier_config).
- Transport is a separate axis: node TLS/mTLS today is
insecure: true (see the inline node config in docker/compose.yml); SPIRE can also source mTLS certs (tls.source.type: spire).
Suggested direction
Notes
This supersedes the inline "debt D1" comment shorthand scattered in slim_client.py/naming.py (that label should get scrubbed in the audit-comments pass, #455). Reference checkout: ../_slim-research/slim-bindings/python/examples/common.py shows all three auth modes end to end.
Move SLIM identity beyond the shared-secret PSK (JWT / JWKS / SPIRE)
Type: enhancement / hardening — hard prerequisite before anything hosted or multi-user.
Current state
SLIM app identity today is a deterministic shared-secret PSK: a per-channel secret is
HMAC-SHA256(master_secret, "workspace/room"), derived independently by every host so no key-exchange round-trip is needed. Passed to the fabric viaservice.create_app_with_secret(name, secret).MYCELIUM_SLIM_MASTER_SECRET, else a public dev literal committed in-repo (mycelium-dev-shared-secret-v1-do-not-use-in-prod) — anyone with the repo can derive it, so it authenticates nothing on its own.MYCELIUM_SLIM_REQUIRE_SECRET=1fails closed on a host that forgot to set a real secret.fastapi-backend/app/services/slim_client.py(~L61-75,mint_shared_secret/resolve_master_secret) and the CLI mirrormycelium-cli/src/mycelium/slim/naming.py.Gaps this creates
workspace/room— the agent segment is ignored — so every member of a room is cryptographically indistinguishable.docs/cross-machine.md): every host must set the same private master secret out of band.What the fabric already supports (from the audit)
slim-bindings exposes a full identity stack we don't touch — verified in the upstream examples (
_slim-research/slim-bindings/python/examples/common.py):IdentityProviderConfig.JWT/IdentityVerifierConfig.JWT,ClientJwtAuth,JwtKeyConfig(JwtAlgorithm.RS256,JwtKeyFormat.PEM|JWKS,JwtKeyData), audience/issuer/subject/duration. Identity = thesubclaim; per-agent identity falls out naturally.IdentityProviderConfig.SPIRE/.SPIREverifier,SpireConfig(socket_path, target_spiffe_id, jwt_audiences, trust_domains). Consumes JWT-SVIDs from the SPIRE Workload API: zero-secret bootstrapping, automatic rotation, workload attestation. Upstream's recommended production setup.create_app_with_secret(...)toservice.create_app(name, provider_config, verifier_config).insecure: true(see the inline node config indocker/compose.yml); SPIRE can also source mTLS certs (tls.source.type: spire).Suggested direction
mycelium hub host/mycelium connect).Notes
This supersedes the inline "debt D1" comment shorthand scattered in
slim_client.py/naming.py(that label should get scrubbed in the audit-comments pass, #455). Reference checkout:../_slim-research/slim-bindings/python/examples/common.pyshows all three auth modes end to end.