From 3a50c447c3ac579907d563f70dde611501557d68 Mon Sep 17 00:00:00 2001 From: mptyl Date: Fri, 14 Aug 2026 16:12:26 +0200 Subject: [PATCH] docs: plan read-only workspace secret implementation --- ...-14-read-only-workspace-runtime-secrets.md | 397 ++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 docs/plans/2026-08-14-read-only-workspace-runtime-secrets.md diff --git a/docs/plans/2026-08-14-read-only-workspace-runtime-secrets.md b/docs/plans/2026-08-14-read-only-workspace-runtime-secrets.md new file mode 100644 index 00000000..b69bc1d2 --- /dev/null +++ b/docs/plans/2026-08-14-read-only-workspace-runtime-secrets.md @@ -0,0 +1,397 @@ +# Read-only Workspace Runtime Secrets Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make workspace consumption strictly read-only while adding installation-scoped Git identity and persistent GUI-managed runtime secrets. + +**Architecture:** Git remains the source of truth and is fetched into an application-owned checkout; complete candidate commits are validated before atomic activation and the backend has no Git write path. Runtime connector credentials are discovered from trusted connector contracts, stored as authenticated ciphertext by a backend vault, and materialized only for the lifetime of diagnostics or runtime leases. The browser exposes repository/readiness status and write-only secret forms without workspace persistence. + +**Tech Stack:** Fastify, TypeScript, Node.js crypto/filesystem, React 18, TanStack Query, Vitest, Go `thothctl`, Docker Compose. + +--- + +### Task 1: Freeze the Git repository boundary to read-only + +**Files:** +- Modify: `backend/src/workspaces/types.ts` +- Modify: `backend/src/workspaces/git-repository.ts` +- Modify: `backend/src/workspaces/registry.ts` +- Modify: `backend/test/workspaces-git-repository.test.ts` +- Modify: `backend/test/workspace-registry.test.ts` +- Modify: `backend/test/workspace-registry-deployment.test.ts` + +**Step 1: Write failing tests** + +Add tests proving that pull never configures a Git author, writes generated files, commits, or pushes; that a malformed candidate leaves the prior active snapshot intact; and that a missing catalog descriptor rejects the whole candidate instead of producing a bootstrap slot. + +**Step 2: Run the focused tests** + +Run: `cd backend && npx vitest run test/workspaces-git-repository.test.ts test/workspace-registry.test.ts test/workspace-registry-deployment.test.ts` + +Expected: FAIL on write/publish behavior and missing-descriptor semantics. + +**Step 3: Implement the read-only boundary** + +Remove `gitAuthorName`, `gitAuthorEmail`, mutation helpers, generated-document reconciliation, publish/conflict types, and bootstrap-slot activation. `pull()` must fetch, validate the complete commit in a candidate snapshot, and replace active state only after validation succeeds. + +**Step 4: Run focused tests** + +Run the command from Step 2. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend/src/workspaces backend/test/workspaces-git-repository.test.ts backend/test/workspace-registry.test.ts backend/test/workspace-registry-deployment.test.ts +git commit -m "refactor: make workspace repository strictly read only" +``` + +### Task 2: Remove publishing and bundle HTTP contracts + +**Files:** +- Modify: `backend/src/routes/workspaces.ts` +- Modify: `backend/test/routes-workspaces.test.ts` +- Modify: `backend/test/workspaces-runtime-v3-boundaries.test.ts` +- Modify: `backend/src/config.ts` +- Modify: `backend/test/workspaces-config.test.ts` + +**Step 1: Write failing route tests** + +Assert `POST /workspaces/publish`, `GET /workspaces/:id/export`, and `POST /workspaces/import` return 404 and that the backend no longer registers multipart or ZIP handling. Assert configuration no longer accepts Git author or bundle-limit settings as workspace-registry fields. + +**Step 2: Run tests and observe failure** + +Run: `cd backend && npx vitest run test/routes-workspaces.test.ts test/workspaces-config.test.ts test/workspaces-runtime-v3-boundaries.test.ts` + +Expected: FAIL because mutation and bundle routes still exist. + +**Step 3: Remove the mutation surface** + +Delete publish/import/export schemas and helpers, remove `multipart`, `yauzl`, and `yazl` usage from the route, and simplify safe workspace errors to read/validate/sync errors. + +**Step 4: Run tests** + +Run the command from Step 2 plus `cd backend && npx tsc --noEmit -p .`. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend/src backend/test package.json package-lock.json +git commit -m "refactor: remove workspace publishing and bundles" +``` + +### Task 3: Expose a sanitized installation repository identity + +**Files:** +- Modify: `backend/src/workspaces/git-repository.ts` +- Modify: `backend/src/routes/workspaces.ts` +- Modify: `backend/test/workspaces-git-repository.test.ts` +- Modify: `backend/test/routes-workspaces.test.ts` +- Modify: `tools/thothctl/internal/config/installation.go` +- Modify: `tools/thothctl/internal/config/installation_test.go` +- Modify: `deploy/psd/thothii-installation.yaml.example` +- Modify: `docs/install/examples/thothii-installation.local.yaml` +- Modify: `docs/install/examples/thothii-installation.server.yaml` + +**Step 1: Write failing parser and status tests** + +Cover HTTPS, SSH URL, and SCP-style remotes; reject embedded user-info for HTTPS; return only `host`, `repository`, `branch`, and `transport`; never return a token, key path, or raw credential-bearing URL. Add installation-descriptor tests for a required `workspaceRepository` block and exactly one read-only transport. + +**Step 2: Run focused tests** + +Run: `cd backend && npx vitest run test/workspaces-git-repository.test.ts test/routes-workspaces.test.ts && cd ../tools/thothctl && go test ./internal/config` + +Expected: FAIL because repository identity and typed installation configuration do not exist. + +**Step 3: Implement safe normalization and installation validation** + +Add the normalized identity to registry status. Extend `thothii-installation.yaml` with remote, branch, and SSH/HTTPS access metadata, validate it against the selected Compose override and environment without reading or returning secret values, and retain the existing environment rendering boundary. + +**Step 4: Run focused tests** + +Run the command from Step 2. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend tools/thothctl deploy docs/install/examples +git commit -m "feat: declare workspace repository in installation config" +``` + +### Task 4: Add the persistent encrypted workspace secret store + +**Files:** +- Create: `backend/src/workspaces/secret-store.ts` +- Create: `backend/test/workspace-secret-store.test.ts` +- Modify: `backend/src/config.ts` +- Modify: `backend/src/app.ts` +- Modify: `compose.yaml` +- Modify: `deploy/compose.local.yaml` +- Modify: `deploy/compose.server.yaml` + +**Step 1: Write failing vault tests** + +Test first-start initialization, atomic blind replacement, deletion, enumeration by configured ID only, AES-256-GCM ciphertext with installation/workspace/field associated data, corruption failure, restrictive files/directories, size limits, and absence of plaintext in persistent bytes. + +**Step 2: Run the vault test** + +Run: `cd backend && npx vitest run test/workspace-secret-store.test.ts` + +Expected: FAIL because `WorkspaceSecretStore` does not exist. + +**Step 3: Implement the vault** + +Create an injectable `WorkspaceSecretStore` backed by an application-managed data root. Persist a versioned encrypted document atomically, generate or load the installation vault key in the private control area, expose only `has`, `put`, `delete`, and scoped materialization operations, and never add a plaintext read API. + +**Step 4: Run tests and typecheck** + +Run: `cd backend && npx vitest run test/workspace-secret-store.test.ts && npx tsc --noEmit -p .` + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend compose.yaml deploy +git commit -m "feat: persist encrypted workspace runtime secrets" +``` + +### Task 5: Derive connector requirements and integrate temporary materialization + +**Files:** +- Create: `backend/src/workspaces/secret-requirements.ts` +- Create: `backend/test/workspace-secret-requirements.test.ts` +- Modify: `backend/src/workspaces/bindings.ts` +- Modify: `backend/src/workspaces/runtime-config-lease.ts` +- Modify: `backend/src/tht/tht-runner.ts` +- Modify: `backend/src/app.ts` +- Modify: `backend/test/workspace-runtime-config-lease.test.ts` +- Modify: `backend/test/workspace-runtime-handoff.test.ts` +- Modify: `backend/test/workspaces-bindings.test.ts` + +**Step 1: Write failing requirement and lifecycle tests** + +Cover PostgreSQL password, REST bearer API key, unauthenticated REST, SSH private key/password, signed HTTP Evidence, and static S3 credentials. Assert temporary files are restrictive, live for exactly one diagnostic/runtime lease, disappear on release and error, and are never persisted in the encrypted vault document. + +**Step 2: Run focused tests** + +Run: `cd backend && npx vitest run test/workspace-secret-requirements.test.ts test/workspaces-bindings.test.ts test/workspace-runtime-config-lease.test.ts test/workspace-runtime-handoff.test.ts` + +Expected: FAIL because requirements still come from installation secret-file paths. + +**Step 3: Implement dynamic requirement resolution** + +Use the selected DWH transport and Evidence authentication contract to map trusted installation-contract suffixes to stable GUI requirement IDs. Overlay materialized temporary file paths only while resolving existing file-oriented connectors, and attach cleanup to every runtime lease. + +**Step 4: Run tests and typecheck** + +Run the command from Step 2 plus `cd backend && npx tsc --noEmit -p .`. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend/src backend/test +git commit -m "feat: resolve workspace secrets from connector requirements" +``` + +### Task 6: Add write-only workspace secret and readiness APIs + +**Files:** +- Modify: `backend/src/routes/workspaces.ts` +- Modify: `backend/src/app.ts` +- Modify: `backend/src/workspaces/types.ts` +- Modify: `backend/test/routes-workspaces.test.ts` + +**Step 1: Write failing API tests** + +Test `GET /workspaces/:id/runtime-configuration`, blind `PUT /workspaces/:id/secrets`, and `DELETE /workspaces/:id/secrets/:requirementId`. Assert strict bodies, limits, unknown-ID rejection, status-only responses, diagnostic invalidation, and `configuration_required`/`ready` state transitions. + +**Step 2: Run tests** + +Run: `cd backend && npx vitest run test/routes-workspaces.test.ts` + +Expected: FAIL because the routes do not exist. + +**Step 3: Implement the routes and readiness projection** + +Inject the secret store into workspace routes and runtime support. Compute per-workspace readiness from active descriptor, current requirement set, configured IDs, and diagnostic generation. Materialize values only inside the diagnostic request and always clean up. + +**Step 4: Run backend gates** + +Run: `cd backend && npx vitest run && npx tsc --noEmit -p . && npm run build`. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add backend +git commit -m "feat: manage runtime workspace secrets through the API" +``` + +### Task 7: Replace workspace management with the two-level read-only UI + +**Files:** +- Modify: `frontend/src/api/workspaces.ts` +- Modify: `frontend/src/api/workspaces.test.ts` +- Modify: `frontend/src/shell/WorkspaceManager.tsx` +- Modify: `frontend/src/shell/WorkspaceManager.test.tsx` +- Delete: `frontend/src/shell/WorkspacePublishDialog.tsx` +- Delete: corresponding publish-dialog tests +- Modify/Delete: `frontend/src/shell/WorkspaceEditor.tsx` and bootstrap-only tests as references permit +- Modify: `frontend/src/workspaces/drafts.ts` +- Modify: `frontend/src/workspaces/drafts.test.ts` + +**Step 1: Write failing UI/API tests** + +Assert the dialog uses at least 60% viewport width and height, shows general repository concepts and exact button consequences at level 1, gates workspace-specific controls on selection, renders requirement explanations and write-only fields at level 2, and has no create/edit/publish/import/export/bundle controls. + +**Step 2: Run focused tests** + +Run: `cd frontend && npx vitest run src/api/workspaces.test.ts src/shell/WorkspaceManager.test.tsx src/workspaces/drafts.test.ts` + +Expected: FAIL on the old draft/publish interface. + +**Step 3: Implement the read-only interface** + +Replace bootstrap editor state with repository status, selection, validation/readiness details, dynamic secret fields, blind save/forget actions, and connection test. Remove workspace draft persistence and clear secret field component state after submit/close. + +**Step 4: Run focused tests and typecheck** + +Run the command from Step 2 plus `cd frontend && npx tsc -b`. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add frontend +git commit -m "feat: add read-only workspace and secret management UI" +``` + +### Task 8: Remove browser-persisted workspace preferences + +**Files:** +- Modify: `frontend/src/workspaces/preferences.ts` +- Modify: `frontend/src/workspaces/preferences.test.ts` +- Modify: `frontend/src/api/sessions.ts` +- Modify: `frontend/src/api/sessions.test.ts` +- Modify: `frontend/src/shell/SteerInput.tsx` +- Modify: `frontend/src/shell/SteerInput.test.tsx` + +**Step 1: Write failing persistence-boundary tests** + +Assert workspace/model/thinking choices are kept only in current application memory or saved through the existing backend settings API, and that no workspace code calls `localStorage`. + +**Step 2: Run focused tests** + +Run: `cd frontend && npx vitest run src/workspaces/preferences.test.ts src/api/sessions.test.ts src/shell/SteerInput.test.tsx` + +Expected: FAIL because preferences still use browser storage. + +**Step 3: Implement ephemeral preferences** + +Replace the storage adapter with an in-memory external store seeded from backend settings. Preserve concurrent workspace-policy gates and session request determinism without persisting selections in the browser. + +**Step 4: Run frontend gates** + +Run: `cd frontend && npx vitest run && npx tsc -b && npm run build`. + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add frontend +git commit -m "refactor: stop persisting workspace state in the browser" +``` + +### Task 9: Update deployment contracts and documentation + +**Files:** +- Modify: `compose.yaml` +- Modify: `deploy/compose.git-ssh.yaml` +- Modify: `deploy/compose.git-https.yaml` +- Modify: `deploy/workspace-registry.env.example` +- Modify: `deploy/psd/operator.env.example` +- Modify: `docs/install/local-workspace-registry.md` +- Modify: `docs/install/server-workspace-registry.md` +- Modify: `docs/guida-utente.md` +- Modify: `scripts/verify-workspace-install-docs.sh` +- Modify: `scripts/workspace-registry-smoke.sh` + +**Step 1: Update executable contract tests first** + +Require read-only Git wording and configuration, repository identity visibility, vault persistence, +and absence of author/push/bundle/browser-secret instructions. + +**Step 2: Run contract tests and observe failure** + +Run: `bash scripts/verify-workspace-install-docs.sh` + +Expected: FAIL against the old manuals and examples. + +**Step 3: Update deployment and manuals** + +Remove Git author settings and write-oriented documentation. Document installation Git bootstrap, +GUI runtime-secret completion, platform-neutral application storage, rotation/forget flows, and +candidate validation semantics. + +**Step 4: Run contract and Go gates** + +Run: `bash scripts/verify-workspace-install-docs.sh && cd tools/thothctl && go test ./...` + +Expected: PASS. + +**Step 5: Commit** + +```bash +git add compose.yaml deploy docs scripts tools/thothctl +git commit -m "docs: describe read-only workspace runtime configuration" +``` + +### Task 10: Full verification and deployed-container refresh + +**Files:** +- Modify only files needed to fix failures found by verification. + +**Step 1: Run static and unit gates** + +```bash +cd backend && npx vitest run && npx tsc --noEmit -p . && npm run build +cd ../frontend && npx vitest run && npx tsc -b && npm run build +cd ../harness && .venv/bin/pytest -q +cd ../tools/thothctl && go test ./... +``` + +Expected: all gates PASS. + +**Step 2: Run deployment contract gates** + +Run: `bash scripts/verify-workspace-install-docs.sh` and the focused workspace registry smoke appropriate to the configured installation. + +Expected: PASS without Git writes or secret disclosure. + +**Step 3: Inspect the final diff and secret scan** + +Run: `git diff --check`, inspect `git status --short`, and search active code/config for removed publish, bundle, Git author, and workspace-localStorage contracts. + +Expected: no whitespace errors, no accidental secrets, and only intended changes. + +**Step 4: Rebuild and restart affected services** + +Use the installation-aware `thothctl` lifecycle for the configured installation to rebuild/restart `core` and `frontend`, then verify health and repository status. Do not restart if no valid local installation descriptor is available; report that external gate explicitly. + +**Step 5: Commit verification fixes** + +```bash +git add +git commit -m "test: verify read-only workspace secret flow" +```