6.2 KiB
Durable Owner Authorization and capability projection
Problem
AIOS already supports unattended worker execution after a Task is executable, but Owner intent was not durable at the capability layer. The same Owner repeatedly had to authorize or repair:
governed-task:project;release:candidate.
This caused valid one-time Owner intent to be lost between conversations and forced repeated human interaction.
Design
Durable authorization is represented by a root-owned mode 0600 policy outside Git. The policy is
persistent until explicitly revoked.
The first persistable capabilities are deliberately narrow:
governed-task:project
release:candidate
They cover preparation work only.
The following capabilities can never be inherited from this durable grant:
submitOwnerApproval
startDeployment
tag_publish
baseline_registry_finalize
runtime_switch
main_write
production_deploy
force_push
credential_admin
Those remain separate high-risk release/Owner-signature gates.
Policy
Example schema:
{
"schema": "aios.owner-durable-authorization.v1",
"grant_id": "owner-permanent-preparation-20260819",
"owner_identity": "owner@example",
"account": "chatgpt-governed-implementer-singlehost",
"capabilities": [
"governed-task:project",
"release:candidate"
],
"issued_at": "2026-08-19T05:00:00Z",
"not_before": "2026-08-19T05:00:00Z",
"persistent_until_revoked": true,
"constraints": {
"repository": "aios/aios",
"allowed_target_prefix": "agent/",
"release_version_prefix": "V2R2C",
"max_task_risk": "engineering"
}
}
The repository never stores the live Owner policy or release credential.
Revocation
A root-owned mode 0600 revocation file lists revoked_grant_ids.
Revocation is checked on every operation, not only at service startup. A running broker therefore fails closed immediately after revocation without needing a new conversation.
Broker boundary
The broker exposes only an AF_UNIX socket:
/run/aios-owner-authorization-broker/broker.sock
The socket is mode 0660 and owned by the service's aios-executor-exchange group. This avoids introducing another long-lived bearer secret solely for local broker access.
It exposes only:
GET /v1/capabilities
POST /v1/project-governed-task
POST /v1/authorize-release-candidate
project-governed-task calls only the canonical Bridge.
authorize-release-candidate persists an Owner authorization decision only. It deliberately does
not invent a local trusted-builder endpoint. The external release connector remains responsible
for projecting the account's real release:candidate scope. Until that external projection exists,
the decision state is AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION.
The broker holds only the existing host control credential needed for canonical governed-task projection. It never holds an Owner signing key or a release/deployment credential. The caller also supplies the expected account identifier over the Unix transport; the OS socket permission is the local access boundary.
Canonical Online Task projection
A durable governed-task:project grant may create a complete canonical Online Task rather than a
bare Task. It requires:
require_online_task_projection=true
generation=1
branch_mode=CREATE_NEW
max_attempts<=3
risk<=engineering
initial action=source_capture
Projected task capabilities are limited to:
git.read
git.test
governed.write.branch
governed.write.file
A write-capable task still begins as source_capture. Existing placeholder-deferral prevents the
generic worker from claiming it until a deterministic plan is registered. Repository mutation
therefore still requires the normal deterministic plan, WriterLease and governed commit path.
This fixes the previous split-brain behavior where a bare Task could exist without the canonical governed execution projection.
External capability projection outbox
A release:candidate authorization now creates a durable machine-readable outbox record:
schema=aios.external-capability-projection.request.v1
state=PENDING_EXTERNAL_CONNECTOR
capability=release:candidate
The projection identifier is deterministic from:
grant_id
account
capability
request_sha256
The outbox stores no credentials or Owner signing material. It retains only the already-authorized release-candidate request, policy/request hashes, account, grant id and timestamps.
The broker exposes read-only Unix-socket endpoints:
GET /v1/projections
GET /v1/projections/{projection_id}
These endpoints re-check the current Owner policy on every read. If the durable grant has been
revoked, enumeration fails closed with OWNER_GRANT_REVOKED. The historical outbox record is
retained for audit and is not silently deleted.
The repository intentionally does not provide a connector acknowledgement/write endpoint yet.
No in-repository code can truthfully assert that the external ChatGPT connector account has gained
release:candidate. Until the platform consumes the outbox and performs its own identity-scope
projection, the only valid state is:
AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION
PENDING_EXTERNAL_CONNECTOR
A future external projector must preserve:
release:candidate
!= submitOwnerApproval
!= startDeployment
!= tag_publish
!= baseline_registry_finalize
!= runtime_switch
Idempotency and audit
Every broker operation requires an idempotency key. A receipt SQLite database stores only:
- idempotency key;
- capability;
- request SHA-256;
- response SHA-256;
- sanitized upstream response;
- timestamp.
An exact retry returns the same receipt. Reusing an idempotency key with different content fails.
Unattended invariant
Once the Owner policy and narrow service credentials are provisioned:
Owner durable grant
→ broker decision
→ canonical governed task projection / release candidate preparation
→ worker / trusted builder
No repeated Owner interaction is required for those preparation capabilities.
Production execution remains:
release candidate
→ immutable approval envelope
→ Owner signature
→ deployment
The durable grant never substitutes for the Owner signature.