KeeperHub / KeeperHub/keeperhub
Workflows cannot reference an organization-scoped variable or secret
- Dominant language
- TypeScript
- Stars
- 24
- Forks
- 93
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 253
Description
Part of #2293. Read the design questions there before starting.
## Reason
There is no organization-scoped value a workflow can reference. Template references are node-scoped by construction: the grammar is `{{@nodeId:Label.field}}` (`lib/workflow/template-ref.ts`) and the runtime resolver resolves a path against that node's recorded outputs (`resolveTemplateToRawValueChecked`, `lib/workflow/executor/executor.workflow.ts:1452`). A trigger is itself a node. There is no other namespace a template can address, and no organization variable or secret table exists in the schema - the only `pgTable`s with `secret` in the name are internal HMAC stores (`schema-agentic-wallets.ts`, `schema-internal-auth.ts`), not a user-facing store.
The consequence is duplication. An API key for a service with no first-class integration, a contract address that differs between an organization's staging and production workflows, a threshold shared across twenty monitors - each has to be typed into every node config that uses it, and edited in every one of them when it changes. The Code node cannot work around this either: the sandbox child is env-scrubbed (`lib/sandbox/child-source.ts:65`), so there is no environment to read from.
## Scope
Organization-scoped key-value entries, in two kinds:
- **Variables** - readable by anyone who can see the organization's workflows, rendered in plaintext in the editor. For non-sensitive configuration.
- **Secrets** - write-only from the UI after creation, masked on read, resolved server-side at execution.
Both referenced from workflow node configs through the existing template syntax.
## What a solution needs to cover
- Storage and encryption at rest for the secret kind, consistent with how integration credentials are handled today.
- Resolution at execution time, server-side. A secret's value must not be sent to the client with the workflow definition.
- The editor surface: creating, listing, updating and deleting entries, and autocomplete in the template picker so a user can discover what exists without leaving the canvas.
- Masking on read, following `components/secret-field.tsx` - the value never reaches the client after it is stored.
- Not leaking a resolved secret into the execution log. `workflow_execution_logs` stores per-step `input` and `output` as jsonb (`lib/db/schema.ts:839`), and there is already a redaction path: `output` is passed through `redactSensitiveData()` for display while `output_raw` holds the unredacted value for cross-process resume. Extend that mechanism rather than inventing a second one, and note that `input` has no equivalent redaction today.
- A decision, stated in the pull request, on whether the Code node can reference secrets. Defaulting to "no" is acceptable and is the safer starting point.
## Out of scope
Per-user values, per-environment overrides, and versioning or rollback of a value. All are reasonable follow-ups; none should block a first version.
---
Tracking: [KEEP-44](https://linear.app/keeperhubapp/issue/KEEP-44) (internal tracker, not publicly accessible)
Contributor guide
Research direction
Read the design questions in #2293 first. Trace template parsing in lib/workflow/template-ref.ts and resolution at lib/workflow/executor/executor.workflow.ts:1452, then inspect schema-agentic-wallets.ts, schema-internal-auth.ts, lib/sandbox/child-source.ts:65, lib/db/schema.ts:839, and components/secret-field.tsx. Done means the scoped entries, editor discovery, secure execution-time resolution, and log redaction requirements are covered without exposing secrets to the client.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, database, frontend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100