Files
aios/docs/gitops/authorized-task-writer-provisioning.md
T

4.0 KiB

Owner Authorization → Task Writer Provisioning Bridge

Purpose

This change closes the control-plane gap where an authenticated Owner can approve a task in the API, but the runtime cannot automatically produce the machine-verifiable, signed, one-time grant required by aios-task-writer-provisioner.

The bridge adds one high-risk endpoint:

POST /api/task-writer/provision-authorized

It does not add a general branch endpoint to the hardened writer. Branch and worktree creation remain the responsibility of the existing root-governed one-shot provisioner. Source commits continue through the isolated writer's existing POST /api/commit-file route.

Trust boundary

The API must authenticate the caller as role=owner and require the writer:provision scope. The request contains an auditable Owner authorization reference; it never contains a private key, token or signature.

The bridge:

  1. Validates an exact repository, base ref, base OID and target branch.
  2. Rejects all production, tag, baseline and direct-main capabilities.
  3. Generates a canonical approval plan and short-lived grant request.
  4. Calls the root-owned signer adapter.
  5. Calls the independent grant validator.
  6. Calls the one-shot task writer provisioner.
  7. Verifies the resulting branch, writer scope and negative policy tests.
  8. Stores a redacted, fsynced idempotency receipt.

The bridge never executes git update-ref, git branch, git push, a tag operation, a baseline mutation or a deployment command directly.

Required runtime configuration

AIOS_GITOPS_REPOSITORY_IDENTITY=aios/aios
AIOS_GITOPS_AUTHORITY_GIT_DIR=/data/aios-gitops/repo.git
AIOS_GITOPS_AUTHORIZED_WRITER_STATE_ROOT=/var/lib/aios-gitops/authorized-writer-bridge
AIOS_GITOPS_GRANT_SIGNER_ADAPTER=/usr/local/libexec/aios-grant-signer-adapter
AIOS_GITOPS_GRANT_VALIDATOR=/usr/local/libexec/aios-grant-validator
AIOS_GITOPS_TASK_WRITER_PROVISIONER=/usr/local/libexec/aios-task-writer-provisioner
AIOS_GITOPS_OWNER_PROVISION_SCOPE=writer:provision
AIOS_GITOPS_GOVERNED_USE_SUDO=true
AIOS_GITOPS_SUDO_PATH=/usr/bin/sudo

When the API service is non-root, AIOS_GITOPS_GOVERNED_USE_SUDO=true uses the fixed /usr/bin/sudo -n -- <governed executable> form. Sudoers must allow only the three exact executables and must not allow a shell, arbitrary environment preservation or arbitrary command paths.

The three governed executables must be absolute, regular, executable, non-symlink files and must not be group/world writable. Each must expose:

--help
--self-test
--grant-dir
--result-json

The signer result must bind approval_plan_id, grant_id, grant_digest_sha256, signing_key_identity and proof that a signature is present. The validator must independently attest the exact repository, base/target bindings, signature, lifetime, operation lists and one-time-use property. The provisioner must return READY_FOR_CODE_AUTHORING, consume the grant, create the exact branch at the exact base OID, start a writer bound only to POST /api/commit-file, and report successful negative policy tests.

Deployment model

Run aios_authorized_task_writer_api.py as the ChatGPT Actions API entrypoint. It delegates all existing routes to aios_chatgpt_actions_api and intercepts only the new provisioning endpoint.

Do not enable this endpoint until:

  • the Owner scope is present in the API authorization registry;
  • the signer adapter uses a runtime secret reference;
  • the validator and provisioner pass their self-tests;
  • the state directory is root-owned and mode 0700;
  • the negative tests show main, other branches, tags, baseline, force push and branch deletion are denied.

Failure behavior

Every mismatch fails closed. A changed main OID, existing conflicting branch, expired or untrusted signature, missing negative test, writer scope expansion, or unconsumed grant returns an error and leaves production state untouched.

Idempotent retries return the existing redacted receipt. A retry with a different task, repository, branch, base or Owner authorization reference is rejected.