Summary
Add a global, runtime-agnostic credential/provider status surface so the dashboard can show, at a glance, which providers and integrations are configured (and where the credential lives) — across all domains, not just image. Status only; never expose or store secret values from the runtime.
This generalizes the per-image work already shipped in the media-generation adapter architecture (see .claude/specs/media-generation-adapter-architecture.md).
Background — what exists today
The image generation work (Phase 1/2 of the media-generation spec) established:
- A runtime-only plugin: image generation goes through
ctx.runtime.images, with a shared @bakin/core/media direct-provider shim composed by the adapter for gap-fill.
- A Bakin-owned provider secret store (
~/.bakin/secrets.json, 0600, env → store resolution) for shim keys and for providers no runtime fronts.
- A "serving path" diagnostic: generation results carry
routeSource/credentialSource; readiness + recommend report a provider-level servedBy (runtime | shim | unconfigured).
So for image providers we can already answer "is this configured, and by whom (runtime vs Bakin)?" The gap: there's no equivalent view for the other credential domains a runtime holds (models/LLM, gateway, channels, skills/tools).
The idea
A Settings surface (a new Credentials tab — not a new nav item) that lists every provider/integration with:
- Configured? yes/no
- Owner / source: runtime-managed (e.g. OpenClaw) vs Bakin-managed (env / secret store)
- Editability: runtime-managed rows are read-only status (point the user to configure them in the runtime); Bakin-managed rows are editable via the existing secret store.
Group by status (Configured / Not configured) to start; category grouping (image / models / channels / tools) can layer on once the real shape is known.
Proposed approach
- New boundary-safe runtime capability — e.g.
runtime.providers.status() — returning a status-only inventory:
{ domain: 'models' | 'image' | 'gateway' | 'channels' | 'skills' | ...,
id: string, owner: 'runtime' | 'bakin', configured: boolean }
No secret values ever cross the boundary — only configured booleans. Surfacing the runtime's own configured self-report is not credential extraction.
- Adapter implements it by normalizing the runtime's config (for OpenClaw, the data
config.get() already returns) + existing capability methods (images.providers()), then stripping everything but status. A future runtime (e.g. Hermes) maps its own shape onto the same contract.
- UI consumes the capability: the Credentials tab merges runtime-reported providers with Bakin-owned env/store entries; editing is limited to the Bakin secret store.
Coverage research (from the repo + mock)
The OpenClaw adapter already reaches these credential-bearing config sections (paths only — these are public config-schema locations, already in the open-source adapter):
| Domain |
Config location |
Configured signal |
| Model/LLM + image providers |
models.providers.<id> (apiKey / env) |
key or env present; image also via images.providers().configured |
| Gateway |
gateway.auth (token) |
token present |
| Channels |
channels.<id> |
per-channel auth present (shape varies, untyped) |
| Skills / tools |
skills.entries.<id> (apiKey / env) |
key or env present (untyped) |
Conclusions:
- No new runtime CLI is required — the inventory is built by normalizing the config the adapter already reads. (The adapter only shells
infer image providers, gateway restart, agents delete, cron run; there is no generic provider-listing command today.)
- It is not image-only — models, gateway, channels, and skills/tools are all reachable.
Unverified / open
- Dedicated TTS / browser / other tool token homes are not clearly located in the typed schema — needs confirmation against a real runtime config or upstream docs.
- Whether a richer first-class provider/credential inventory exists upstream (vs. normalizing config).
- Multi-runtime mapping (the capability must survive a second runtime with a different shape).
- Tab axis: status (Configured/Not) vs owner (runtime/Bakin) vs category — owner is arguably the most actionable; decide with real data.
Constraints (non-negotiable)
- Status only. The inventory returns
configured booleans; it must never read, return, log, or persist runtime-owned secret values.
- Runtime owns its keys. Bakin surfaces runtime status and points to where to configure it; it does not write runtime credentials.
- Bakin only manages its own store. Editable rows map to
~/.bakin/secrets.json (already 0600, write-only/masked API to be added).
Near-term scope decision
Ship the image-scoped Provider Keys tab first, driven by the existing image providerReadiness() (runtime/env/store/none per image provider; edit = Bakin store). This issue tracks generalizing it into the runtime-agnostic inventory above.
References
.claude/specs/media-generation-adapter-architecture.md (four-layer model, secret store, serving-path diagnostics)
packages/core/src/media/ (shared shim + secret store)
packages/adapter-openclaw/src/config.ts, runtime.ts (config reader + image capability)
Summary
Add a global, runtime-agnostic credential/provider status surface so the dashboard can show, at a glance, which providers and integrations are configured (and where the credential lives) — across all domains, not just image. Status only; never expose or store secret values from the runtime.
This generalizes the per-image work already shipped in the media-generation adapter architecture (see
.claude/specs/media-generation-adapter-architecture.md).Background — what exists today
The image generation work (Phase 1/2 of the media-generation spec) established:
ctx.runtime.images, with a shared@bakin/core/mediadirect-provider shim composed by the adapter for gap-fill.~/.bakin/secrets.json,0600, env → store resolution) for shim keys and for providers no runtime fronts.routeSource/credentialSource; readiness + recommend report a provider-levelservedBy(runtime|shim|unconfigured).So for image providers we can already answer "is this configured, and by whom (runtime vs Bakin)?" The gap: there's no equivalent view for the other credential domains a runtime holds (models/LLM, gateway, channels, skills/tools).
The idea
A Settings surface (a new Credentials tab — not a new nav item) that lists every provider/integration with:
Group by status (Configured / Not configured) to start; category grouping (image / models / channels / tools) can layer on once the real shape is known.
Proposed approach
runtime.providers.status()— returning a status-only inventory:configuredbooleans. Surfacing the runtime's ownconfiguredself-report is not credential extraction.config.get()already returns) + existing capability methods (images.providers()), then stripping everything but status. A future runtime (e.g. Hermes) maps its own shape onto the same contract.Coverage research (from the repo + mock)
The OpenClaw adapter already reaches these credential-bearing config sections (paths only — these are public config-schema locations, already in the open-source adapter):
models.providers.<id>(apiKey / env)images.providers().configuredgateway.auth(token)channels.<id>skills.entries.<id>(apiKey / env)Conclusions:
infer image providers,gateway restart,agents delete,cron run; there is no generic provider-listing command today.)Unverified / open
Constraints (non-negotiable)
configuredbooleans; it must never read, return, log, or persist runtime-owned secret values.~/.bakin/secrets.json(already0600, write-only/masked API to be added).Near-term scope decision
Ship the image-scoped Provider Keys tab first, driven by the existing image
providerReadiness()(runtime/env/store/none per image provider; edit = Bakin store). This issue tracks generalizing it into the runtime-agnostic inventory above.References
.claude/specs/media-generation-adapter-architecture.md(four-layer model, secret store, serving-path diagnostics)packages/core/src/media/(shared shim + secret store)packages/adapter-openclaw/src/config.ts,runtime.ts(config reader + image capability)