Credential Source Pattern (cross-cutting)
Status: Open — design pattern (Phase 5, Agent Orchestrator Research Program)
Problem
Section titled “Problem”jackin’ already has several credential-resolution paths, documented in Environment Variables and Authentication. They do not yet share one typed credential-source abstraction.
Several upcoming items need more sources:
- GitHub link tracking needs a GitHub PAT.
- Task source abstraction may need per-source tokens.
- Claude auth strategy needs a more durable Claude OAuth flow.
- A future Linear/JIRA/etc. source needs whatever those services use.
If each item adds its own source resolution code, drift is guaranteed. The right move is a single cross-cutting pattern that any feature can plug into.
This isn’t a feature that ships on its own — it’s a refactor that unblocks several other features. The leaf exists so the design call is made once, in one place, instead of being rediscovered five times.
Why It Matters
Section titled “Why It Matters”- Without a unified pattern, adding a new credential source (Apple Keychain, Linux secret-tool, AWS Secrets Manager, Vault) means touching every consumer.
- multicode has already tried three sources (
env,command, keychain) for one credential (their GitHub PAT) and converged on a small consistent shape. Borrowing the shape is much cheaper than designing one from scratch. - It pairs with jackin-remote and the future Kubernetes platform — both of which need to know how to resolve credentials locally and forward them remotely.
Inspiration in multicode
Section titled “Inspiration in multicode”Sources:
- README — Authentication (documents all three sources — keychain, env, command — plus
populate-git-credentials) - Config —
config.toml[github]block - Source —
lib/src/services/config.rs(TOML deserializer for the tagged-union token form)
[github]# One of three forms:token = { env = "GITHUB_TOKEN" }token = { command = "gh auth token" }token = { keychain-service = "multicode.github", keychain-account = "github-mcp-token" }multicode also exposes populate-git-credentials = true for
auto-injecting the token as a git credential helper inside the
container.
The shape: a tagged-union TOML value with multiple resolver backends,
each backend implementing a small trait (fn resolve() -> Result<SecretString>).
Resolution happens at jackin-process startup (or per-invocation,
depending on the consumer).
Recommended Shape
Section titled “Recommended Shape”A CredentialSource type that’s accepted anywhere a token/secret is
needed in TOML. The resolver vocabulary is small and explicit; no
implicit chains.
TOML shape
Section titled “TOML shape”# Direct literal — convenience, NOT recommended for real secretssome_token = "sk-actual-value"
# Tagged formssome_token = { env = "MY_VAR" }some_token = { command = "gh auth token" }some_token = { op = "op://Vault/Item/Field" }some_token = { os_store = "jackin.token", account = "default" }some_token = { file = "~/.secrets/token" }Six backends:
- Literal (string) — for non-secret values; emits a warning if a
string field tagged
#[credential]is supplied as a literal in any operator-config file outside~/.config/jackin/local.toml. env— read host-operator env var; error if unset. This is never broad environment inheritance into the agent container.command— run a command without a shell by default, with a minimal environment; the command must exit 0 and may only return the secret on stdout.op— route to 1Password CLI using the current operator-env behavior.- OS secret stores — Apple Keychain via
securityon macOS, libsecret/secret-toolon Linux, and Windows Credential Manager when supported. New backend. file— read file contents. Useful for K8s secret mounts and local development.
Resolution behavior
Section titled “Resolution behavior”- Resolution happens at the consumer’s first need (lazy), not at config load. This means a misconfigured backend errors only when the feature using it actually runs.
- Resolved values implement
SecretString(zeroize-on-drop, no Display impl, redacted in debug logs). - Failures are explicit and contextual:
GitHub PAT (configured via [github].token = { command = ... }) failed: <stderr from command>. - Resolved values are not cached across runs of
jackin. Within one run, they may be cached per-source-instance.
Consumers
Section titled “Consumers”Each consumer (GitHub link tracker, task source, Claude auth, future
ones) accepts Option<CredentialSource> in its config and resolves
when it needs the value. No consumer reaches around the abstraction.
Forwarding to containers
Section titled “Forwarding to containers”populate_git_credentials (multicode’s name) generalizes to a
per-credential forward_to_container = true flag on the consumer’s
config block. Implementation: jackin’ writes the resolved secret into
a container env var or file mount per the consumer’s spec, never to
the agent’s persisted state directory.
Scope (V1)
Section titled “Scope (V1)”CredentialSourceenum + TOML deserialization.- Six backends: literal, env, command, op, OS secret store, file.
SecretStringnewtype with zeroize-on-drop.- Replace existing ad-hoc credential lookups with the unified type while preserving the behavior documented under Environment Variables and Authentication.
- New consumers (GitHub link tracking, task sources) accept
CredentialSourcefrom day one. - Documentation page: “Credential sources” — explains all six backends, security tradeoffs, recommended pattern per use case.
- Vault / AWS Secrets Manager / GCP Secret Manager backends. Defer until a real user asks; the file-mount pattern + K8s secrets covers most of the space.
- Per-source resolution caching with TTL. Lazy-once-per-run is enough for V1.
- A
jackin secrets testCLI that resolves every configured credential and reports status. Useful but small; defer.
Open Questions
Section titled “Open Questions”- Literal-as-secret warning. Should jackin’ refuse to load a
literal where a
CredentialSourceis expected (with override flag), or just warn loudly? Recommended: warn loudly; the convenience for non-secret tokens (e.g. a Slack webhook URL) outweighs strictness. - OS secret-store implementation. macOS Keychain and Linux
secret-toolare the first practical targets; Windows Credential Manager should share the same resolver shape once a Windows host story matters. - Forwarding interaction with Claude auth strategy. That item is already considering token plumbing; this pattern should be the abstraction it uses, not a parallel design. Recommended: make this leaf land first so the Claude auth work has the type to use.
Related Files
Section titled “Related Files”- New module (e.g.
src/credential.rs) —CredentialSourceenum + resolver src/config/mod.rs— TOML deserialization- Existing
op://resolution path — refactored to use the unified type - Future consumers — link to this leaf as their credential plumbing
- New docs page:
docs/src/content/docs/guides/credentials.mdx
See Also
Section titled “See Also”- Agent Orchestrator Research Program
- 1Password integration — existing consumer; refactored to use the new type
- Claude auth strategy — existing consumer; refactored to use the new type
- GitHub link tracking — new consumer
- Task source abstraction — new consumer
- jackin-remote — credential forwarding model