feat(worker): attach server-defined credentials to terminal sessions #350
No reviewers
Labels
No labels
Epic 5
MCP
P1
PR-0
PR-1
api
api-layer
architecture
area:backend
area:frontend
arrow
authentication
backend
bug
chat
client
clipboard
common-module
compose
cross-cutting
cross-cutting-requirement
data-layer
data-models
database
desktop-client
documentation
duplicate
enhancement
epic
epic-1
epic-2
epic-3
epic-4
epic-5
epic-6
epic-7
epic-llm-config
error-handling
feature
frontend
good first issue
help wanted
implementation
infrastructure
installer
invalid
kotlin
ktor
llm
medium-priority
message
multiplatform
needs-review
needs-triage
packaging
performance
phase-1
phase-2
phase-3
phase-4
phase-5
phase3
phase:3
priority: high
question
refactor
refactoring
resource-sharing
security
server
server-module
services
session
session-management
settings
setup
shutdown
state
storage
streaming
sub-issue
tests
tooling
type:task
ui
ui/ux
user-accounts
user-management
ux
viewmodel
websocket
windows
windows-integration
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Torvian.eu/chatbot!350
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/terminal-session-credential-attach"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lets an LLM attach an operator-defined credential profile to an existing
terminal_session, so an interactivessh,gitor password-prompting command can authenticate without the model ever seeing the secret. A profile fixes the command, its arguments and how the secret reaches the process; the model can only name a profile. The worker runs the profile as a second, dedicated operating-system account (vault), decrypts the secret only for that process, and sweeps the material when the session returns to its shell. The branch also carries two follow-ups required for this to be safe: the worker records the numeric worker id the server assigned at registration, and the wildcard environment passthrough into isolated children was removed.Changes
common: the credential profile, secret and write-result DTOs with their channel rules and limits, the two new resources, six protocol command types with their payloads and mappings, and theattach/detachactions on theterminal_sessiontool schema.server: migrationV37with profile, secret and per-device signature tables; DAOs, entities and mappers; an encryption service that wraps a random data key to the worker certificate; CRUD services behind user JWT; a dispatch service that pushes each write to the worker best-effort after commit; the worker bootstrap endpoint; and a generatedcredential_profilesinstruction that advertises the profiles of the workers a role'sterminal_sessiontools target.app: a Credentials settings tab (profiles, secrets, worker picker, masked secret input with a show/hide toggle), its repository, signing API client and view model, with a visible warning when a save could not be pushed to a worker; and rendering of the new instruction kind.worker: an in-memory credential cache filled at bootstrap and kept current over the protocol; a signature-verifying profile validator; lazy decryption bound to the worker certificate; a helper-mediated per-attach secret tree; askpass, env and file delivery; the vault account, its helper and the shared privilege-drop header; the attach/detach state machine in the terminal session; the worker-id pin written by setup; and attach state reported in the tool output so the model can see the transition and the profile exit code.deploy/worker:run-as-vault.c,privilege_drop.h, the askpass helper, thevaultaccount and secret root in the image, entrypoint remapping and collision checks, tmpfs and stop-grace in the compose examples, and README documentation.Implementation notes
vaultaccount is the isolation boundary. It must differ from both the worker account and theagentaccount, is checked at startup by what the helper reports rather than by name, and the helpers areroot:torvian 04750so anagent-driven session cannot execute them. The vault child never receives the worker private key, the worker environment or the workspace as its working directory."*"entry is gone fromcommandIsolation.environment.allowPassthrough, and a name carrying the worker's own private key or setup password is refused in both passthrough fields. This is a breaking configuration change: an operator who relied on"*"will now get a startup error and must list the names explicitly. It needs a release note.V37only adds tables, so a downgrade leaves them unused;worker_credential_profile.secret_refhas no foreign key on purpose, and the SQL says why.PemPrivateKeyLoaderextraction travels with the decryption change, because the challenge signer and the decryptor are its two consumers.Validation
./gradlew :common:desktopTest— 231 tests, 0 failures../gradlew :worker:test— 792 tests, 4 skipped, 0 failures../gradlew :server:test— 2420 tests, 1 skipped, 0 failures../gradlew :app:desktopTest— 1120 tests, 2 skipped, 0 failures.gcc -O2 -Wallforrun-as-agentandrun-as-vault, so the C helpers compile and link.vaultaccount, received its secret through the helper-mediated secret tree, exited with a non-zero code, and the session returned to its shell with the material released.What is still not covered:
deploy/worker/README.mdhas not been run in full: it still coversrun-as-vault id -unversusrun-as-agent id -un, anagentsession being denied execution of the helpers, tmpfs behaviour across a restart, and shutdown cleanup under a stopped worker.409re-setup path are untested outside unit tests.Risks and follow-up
"*"inallowPassthroughwill now get a startup error; this is intentional and needs a release note.detailsbefore they reach the model; persistingdetailsis separate future work.