2.9 KiB
2.9 KiB
id, title, status, wave, depends_on, plan
| id | title | status | wave | depends_on | plan | |
|---|---|---|---|---|---|---|
| task-02 | Filesystem registration store + credentials | done | 1 |
|
docs/plans/plugins/plan.md |
Filesystem registration store + credentials
Title
Persist plugin registrations to ~/.kandev/plugins/{id}.yml with a bcrypt-hashed
api_key and an encrypted (recoverable) webhook secret, plus operator config at
{id}.config.yml.
Inputs
- Spec
docs/specs/plugins/requirements/plugins.md→ "Plugin registration (filesystem-backed)" and "Persistence guarantees". - Credential storage refinement (resolves a spec inconsistency — capture in the
task-15 spec update):
api_key: kandev only ever verifies inbound requests → store as bcrypt hash only. Cleartext returned once at registration.webhook_secret: kandev must HMAC-sign outbound event deliveries → bcrypt (one-way) is impossible. Store the secret in the encrypted secrets store (internal/secretsviainternal/integrations/secretadapter.Adapter, which is reversible viaReveal). The{id}.ymlkeeps only a secret reference id, not the value. Cleartext returned once at registration.
- Uses
Manifestfrom task-01 (importinternal/plugins/manifest). - bcrypt:
golang.org/x/crypto/bcrypt. Secret adapter interface: inject a small interfaceSecretVault { Set(ctx,id,name,value) error; Reveal(ctx,id) (string,error); Delete(ctx,id) error }so the store is testable with a fake (the real one issecretadapter.Adapter).
Acceptance
Recordstruct = manifest + runtime fields (status, api_key_hash, webhook_secret_ref, registered_at, last_health_check).store.Storeinterface:List,Get(id),Save(*Record),Delete(id),GetConfig(id),SetConfig(id, map).NewFSStore(dir string, vault SecretVault);Register(ctx, manifest) (*Record, Credentials, error)generates randomapi_key+webhook_secret(crypto/rand), bcrypt-hashes the api_key, stores the webhook_secret in the vault under refplugin-webhook-secret:{id}, writes{id}.yml, returns cleartextCredentials{APIKey, WebhookSecret}once.VerifyAPIKey(id, key) (bool, error)(bcrypt compare);RevealWebhookSecret(ctx, id) (string, error)(vault Reveal).- Duplicate id on Register → typed
ErrAlreadyExists.Deletealso deletes the vault secret.
Files
apps/backend/internal/plugins/store/store.goapps/backend/internal/plugins/store/fs_store.goapps/backend/internal/plugins/store/fs_store_test.go(t.TempDir()+ fake vault)
Verification
go test ./internal/plugins/store/...fromapps/backendmake -C apps/backend lint
Output contract
Report: file format, the api_key(bcrypt)/webhook_secret(encrypted-ref) split, the
SecretVault interface shape (so task-05/06 can reveal the secret for signing).
Do not edit internal/plugins/manifest/ (import only). Stay within internal/plugins/store/.
Dependencies
task-01 (imports manifest types).