docs/implementation-runbook.md
Rama main · réplica de lectura del repositorio original.
# Implementation runbook
> Historical build path. The broad Scoped runtime, durable state/jobs, integrated OAuth and optional elevation code now exist. Use this document to understand implementation sequencing, not as the current project status. Current status lives in [project-status.md](project-status.md); current productization work must not reopen completed gates without concrete evidence.
This is the detailed build path that guided implementation. The canonical architecture is docs/architecture.md and the gate sequence is docs/mvp-first.md.
## 0. Ground rules
Before code:
1. read docs/README.md
2. complete Gate -1
3. complete Gate 0A
4. do not build beyond the current gate
5. use Go for both reference binaries
6. keep Gateway unprivileged and Broker local-only
## 1. Repository layout
Reference layout:
~~~text
cmd/
vps-agent-gateway/
vps-agent-broker/
internal/
gateway/
mcp/
auth/
tools/
brokerclient/
broker/
api/
authz/
policy/
files/
systemd/
docker/
exec/
jobs/
state/
secrets/
audit/
shared/
protocol/
errors/
policy/
schema.json
controlled.yaml
scoped.example.yaml
full.example.yaml
deploy/
systemd/
reverse-proxy/
tests/
integration/
security/
~~~
Runtime:
~~~text
/etc/vps-agent/ config/secrets metadata
/run/vps-agent/ broker.sock
/var/lib/vps-agent/ state/jobs/audit
~~~
## 2. Gate 0B implementation
Build only vps-agent-gateway.
Use the official MCP Go SDK and Streamable HTTP.
Tools:
~~~text
system.info
file.read_test
file.write_test
~~~
Filesystem root:
~~~text
/tmp/vps-agent-poc/
~~~
Requirements:
- non-root service account
- no Docker socket
- no SQLite
- no generic shell
- input schemas
- output size limits
- path confinement tests
- MCP Inspector test
Use an internal Executor interface so Gate 0B's local test executor can later be replaced by BrokerClient without rewriting tool handlers.
## 3. Gate 1 Broker
Add vps-agent-broker.
### IPC
Use a Unix Domain Socket.
Recommended:
~~~text
/run/vps-agent/
owner: root
group: vps-agent
mode: 0750
/run/vps-agent/broker.sock
owner: root
group: vps-agent
mode: 0660
~~~
Check peer credentials where practical.
### First Broker operations
~~~text
ping
system.info
service.status
service.restart
~~~
Allow only one explicitly configured non-critical unit.
Broker authorization is deny-by-default.
### Gate 1 audit
Structured journald or append-oriented JSON is sufficient.
Capture:
- request/action ID
- subject
- canonical tool
- canonical resource
- decision
- result/exit status
- duration
Do not log raw secrets.
## 4. Gate 2 Scoped pilot
Add policy parsing/validation inside Broker.
Unknown fields/capabilities fail closed.
Define one real stack.
Possible tools:
~~~text
file.read
service.status
service.restart
docker.logs
docker.action
job.status
~~~
Do not add unrestricted Docker socket access.
Docker remains Broker-only and typed.
Run for several days and record real missing capabilities.
## 5. Gate 3 durable state
Only now add SQLite if needed.
Only Broker opens:
~~~text
/var/lib/vps-agent/state.db
~~~
Enable WAL.
Suggested state:
- jobs
- idempotency / operation journal
- resource locks
- audit metadata
- later approvals/grants
Rules:
- short transactions
- no external command inside DB transaction
- busy timeout/backoff
- logical locks with deadlines for long operations
- every resource lock has owner/action_id + monotonically increasing fencing token
- stale owners cannot release or commit against a newer fencing token
### Operation journal
Before a replay-protected external side effect:
1. insert/claim the invocation as `PENDING`
2. commit the short transaction
3. perform the external effect
4. mark `DONE` and persist the result
If the process crashes between steps 3 and 4, the next request returns `reconcile-required` or runs an action-specific reconciler. It must not blindly repeat the effect.
### Idempotency
Gateway/integration layer creates retry identity as infrastructure metadata when a stable retry identity exists.
Bind to:
~~~text
subject + canonical tool + normalized request hash + invocation identity
~~~
Never infer retry merely because arguments are identical.
## 6. Jobs
Internal model:
~~~text
job.start
job.status
job.tail
job.cancel
~~~
A job runs in its own managed systemd unit/cgroup when appropriate.
Persist:
- owner
- policy/grant
- resource
- action
- unit/pid
- created/deadline
- status
- exit code
The MCP connection may disappear without losing job state.
## 7. Secrets
Prefer:
- root-owned files outside repo
- systemd LoadCredential / credential directory
Broker resolves secret references.
No plaintext secret retrieval tool.
Test that stdout/stderr/audit do not trivially expose known test secrets.
## 8. Gate 4 broader writes
Add only demonstrated capabilities.
### Files
Implement safe resolution and atomic writes.
Preferred openat2; secure fallback or fail closed.
For configuration changes:
~~~text
backup/temporary write
-> native validator
-> atomic replace
-> reload/restart
-> healthcheck
-> rollback when safe
~~~
Examples:
~~~text
nginx -t
docker compose config
systemd unit validation
~~~
### Sandboxed shell.exec
Only if typed tools are insufficient.
Run under systemd transient unit/cgroup.
Enforce:
- cwd roots
- timeout
- MemoryMax
- TasksMax
- output limit
- cancellation
- filtered environment
- network policy
Landlock is optional defense-in-depth.
## 9. ChatGPT authentication/distribution
This stage is implemented for the supported integrated self-hosted OAuth path. Follow docs/chatgpt-integration.md and docs/authentication.md.
The implementation validates active token state, issuer, dedicated audience/resource, expiration, subject and required scopes through private introspection, while the Broker rechecks the expected subject and policy.
## 10. Gate 5 elevation
Optional elevation machinery exists and is acceptance-tested, but it does not constitute a Full/R5 production claim. Full remains disabled by default and Scoped remains the supported product path.
Historical gate rule: only add elevation after Scoped proves insufficient.
Enable feature:
~~~yaml
features:
full_mode_enabled: true
~~~
Add:
- elevation request
- operator out-of-band approval
- one-time nonce
- temporary explicit capabilities
- expiry
- revoke-all
- separate network elevation
- enhanced audit
An operator approval UI may be hosted by Gateway, but Broker validates the signed operator identity/assertion and nonce independently.
Only after this is tested consider shell.exec_admin.
## 11. Lease and job semantics
Default:
~~~text
job_deadline <= grant_expiry
~~~
When an elevated grant expires:
- no new privileged mutations
- graceful termination
- force termination after bounded grace period
- persist final state
- audit
A special completion grant may be used only with explicit operator approval.
## 12. Recovery
### Broker restart
- reconcile persisted jobs with systemd units/processes
- adopt monitoring or terminate according to policy
### SQLite corruption
- fail closed
- invalidate pending approvals
- treat elevated grants as revoked
- preserve damaged DB
- restore verified backup
- reconcile active jobs
- re-enable after integrity checks
### Emergency
Provide an operator command outside the AI surface:
~~~text
vps-agent revoke-all
~~~
It revokes elevated grants and blocks new elevated starts.
## 13. systemd hardening
Gateway:
- User=vps-agent
- NoNewPrivileges=yes
- ProtectSystem=strict
- ProtectHome=yes
- PrivateTmp=yes
- only required ReadWritePaths
- no Docker socket
Broker:
- root-owned
- no TCP listener
- Unix socket only
- minimal API
- fixed config/runtime paths
- journald
- restart-on-failure
Review with systemd-analyze security.
## 14. Production acceptance
### Scoped production
Require at least:
- Gate 0A/0B/1/2 passed
- negative authorization tests
- safe filesystem tests for enabled writes
- recovery test
- secrets test
- client path proven
- existing workloads survive Gateway/Broker stop
### Full production
Additionally:
- Full explicitly enabled
- approval tested
- expiry tested
- revoke-all tested
- network capability separation tested
- remote audit checkpointing tested
- admin shell containment/recovery tested
## 15. Rollback
The agent gateway must remain an optional control plane.
Stopping Gateway/Broker and removing its route must not stop existing application workloads.
That property is mandatory.