Skip to content

feat(dgw): keep provisioner task secrets in the provisioning store - #2058

Open
irvingouj@Devolutions (irvingoujAtDevolution) wants to merge 4 commits into
feat/ai-analysis-pipelinefrom
feat/provisioning-task-secrets
Open

irvingouj@Devolutions (irvingoujAtDevolution) wants to merge 4 commits into
feat/ai-analysis-pipelinefrom
feat/provisioning-task-secrets

Conversation

@irvingoujAtDevolution

@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

The provisioning store, which already holds the injected credentials and connection options DVLS hands Gateway for a session, can now also hold the secrets of provisioner tasks: something a task needs while it runs but that must never be written to disk. The first one is the API key of an AI analysis (#2044).

This is the third kind of provisioned material, with its own policy, as credential/INTENT.md asks ("the policy for different kinds of provisioning should be defined on a per-kind basis"):

  • keyed by Task ID, independently of the JTI-keyed session halves
  • a new secret for the same Task replaces the old one
  • removed when the Task ends (remove_task_secret) or at its expiry, by the existing cleanup task
  • encrypted on the way in with the same in-memory master key as the injected credentials (ChaCha20-Poly1305, key in protected memory), and decrypted into a zeroizing SecretString only when read
  • opaque: the store doesn't look inside, so each task decides what its secret holds

The session halves (credentials and connection options) are unchanged. credential gains encrypt_secret / decrypt_secret, thin entry points over the existing master key.

Where to look

  • provisioning.rs: insert_task_secret, task_secret, remove_task_secret, and the cleanup (remove_expired, next_expiry)
  • credential/mod.rs: the two entry points

Tested: provisioning tests 19/19, incl. a secret read back but never held in clear, removal and expiry through the cleanup, and session credentials untouched by task secrets.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Caller-controlled timestamps can bypass task-secret expiry enforcement.

1 open finding
What changed in this PR

Adds encrypted, in-memory storage for provisioner task secrets.

Changes:

  • Adds task-secret insertion, retrieval, removal, expiry, and cleanup.
  • Exposes credential encryption helpers for arbitrary secrets.
  • Adds lifecycle and isolation tests.
File Description
devolutions-gateway/​src/​provisioning.rs Implements the task-secret store and tests.
devolutions-gateway/​src/​credential/​mod.rs Adds secret encryption and decryption helpers.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread devolutions-gateway/src/provisioning.rs Outdated
The provisioning store gains a third kind of provisioned material,
next to the injected credentials and the connection options: the
secrets of provisioner tasks, such as the API key of an AI analysis.
A provisioner task needs them while it runs, but they must never be
written to disk.

Task secrets are keyed by Task ID and follow their own policy, as the
credential intent asks for each kind of provisioning: a new secret
replaces the old one, and a secret goes away when its Task ends or at
its expiry, through the existing cleanup task. Like the injected
credentials, they are encrypted on the way in with the in-memory
master key, and decrypted into a zeroizing value only when read. The
store treats a secret as an opaque string, so each task decides what
it holds.

The session halves are unchanged.
task_secret took the current time from its caller, so a caller passing an earlier time could still read an expired secret. It now reads the clock itself.
Keep the existing `ProvisioningStore` paragraphs as they were and only add
the task-secrets paragraph, and describe the task secret methods by what
they guarantee rather than by what the AI analysis caller stores.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The store keeps an entry until it is removed or expires; removing it when
  the Task ends is the caller's job.
- `insert_task_secret` takes the secret in clear and encrypts it.
- The tests are named after what they check: the Debug output never shows
  the secret, and the cleanup pass, not the cleanup task, removes it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants