Skip to content

SLIM identity: move beyond shared-secret PSK to JWT/JWKS/SPIRE (prereq for hosted/multi-user) #476

Description

@juliarvalenti

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 + JWKSIdentityProviderConfig.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 / SPIFFEIdentityProviderConfig.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

  • Add a JWT/JWKS identity path behind a config seam, keeping shared-secret as the local-dev default.
  • Evaluate SPIRE for the hosted/multi-user target (recommended upstream); scope what a SPIRE agent + trust domain looks like in our deploy.
  • Turn on node TLS for any non-localhost topology (pairs with mycelium hub host / mycelium connect).
  • Define per-agent identity + revocation semantics on top of real identity tokens.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions