Skip to content

feat(orchestrators): add Agor+acq sandbox integration (Tier 1 worked example) - #284

Draft
mogul wants to merge 11 commits into
mainfrom
feat/agor-integration-v2
Draft

feat(orchestrators): add Agor+acq sandbox integration (Tier 1 worked example)#284
mogul wants to merge 11 commits into
mainfrom
feat/agor-integration-v2

Conversation

@mogul

@mogul mogul commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Context

Tier 1 worked example of an Agor + acq sandbox-isolation integration
(map #247): a runnable
executor_command_template wrapper that runs an Agor executor inside an acq
sandbox, a small agor-daemon-egress kit the wrapper requires, a new
integrations/orchestrators/ area with its ADR, and the two source exploration
docs. Zero Agor code changes for the OpenCode path.

All decision/build child issues are closed and AFK-confirmed by the human owner
(#248#256, #258, #259, #261). This PR rebases that work cleanly onto current
main.

AI assistance disclosed: prepared with an AI coding agent (OpenCode);
human-owned and reviewed.

Plan (what changed, and how it maps to the approved plan)

The prior feat/agor-integration branch also carried 6 openchamber commits
that are a parallel implementation of the "startup owns server" feature that
main has since landed authoritatively (#246 + #283). Rather than merge and
resolve 13 openchamber conflicts, this branch is a fresh cut of origin/main
with only the 8 Agor-deliverable commits cherry-picked; the obsolete
openchamber commits were dropped. main's openchamber remains authoritative.

Files (+2,113 / 14 files, no openchamber files touched):

  • integrations/orchestrators/ — new area: README.md, area ADR
    0001-orchestrators-area-and-agor-acq.md, agor/README.md (setup guide),
    agor/sandbox-wrapper-acq.sh, two exploration docs.
  • integrations/isolation/acq-kits/agor-daemon-egress/ — new egress kit
    (spec.yaml, README.md, TROUBLESHOOTING.md, scripts/verify, kit ADR)
    • kits.yaml registry row + acq-kits/README.md link.
  • integrations/README.md — orchestrators area row.

Two additions beyond the cherry-picks:

Verification

Run in the project's standard environment (Python 3, shellcheck 0.11.0):

Check Result
validate-kits.py --strict PASS — 6/6 kits OK; registry cross-check passes
validate_repo.py PASS — 38 patterns, all refs resolve, no cycles
scan_unsafe_shell.py . PASS — 0 errors, 0 warnings
generate_index.py --check PASS — INDEX.yaml up to date
generate_catalog.py --check PASS — CATALOG.md up to date
shellcheck (wrapper + verify) PASS — clean
egress kit vs kit-hybrid-v1 schema PASS — validates with provenance
provenance/drift assertions (manual*) PASS — malformed provenance rejected; usai bundle set unchanged

* pytest is not installable in this container (no ensurepip/venv); the
provenance-relevant assertions from scripts/tests/test_kit_provenance_and_drift.py
were exercised directly with jsonschema. CI runs the full pytest suite.

Live wrapper dry-run against real acq (quickstart main): remote /
Agor-managed payload emits the correct plan
(acq create shell <worktree> <repo.git> --name agor-… --kit <egress> … | acq exec … -- agor-executor --stdin,
then acq rm on exit); a worktree whose main repo is outside AGOR_DATA_HOME
is correctly refused (exit 5).

CLI-surface re-verify vs current quickstart main (since #248/#249 closed,
quickstart landed #233 sbx↔msb parity and #257 fail-closed run): acq create
positional agent+workspaces + --name + repeatable --kit; acq exec <name> -- CMD stdin passthrough; acq secret set <SANDBOX> usai reads value from stdin
with positional scope; sbx-v2 synthesis preserves caps.network.allow
host:port. The v1 contract still holds.

Live end-to-end validation (#257): BLOCKED

Per the isolation area ADR, a live acq run cannot execute inside an sbx
sandbox (no nested sandboxes). This CI/dev host is itself a Docker container
(/proc/1/cgroup0::/docker/…), and sbx is not installed (commercial;
brew install docker/tap/sbx + sbx login), msb is absent with no
/dev/kvm
, and agor/agor-executor are absent (no daemon to spawn the
executor or mint the scoped JWT). Recorded BLOCKED per AGENTS.md §8.3 rather
than fabricating a pass. Retry trigger: a sandbox-capable, non-nested host —
sbx (installed + logged in) and/or msb (KVM host) — with a running Agor
daemon on :3030 + an out-of-band USAi key.

Rollback

Revert this PR (single squash commit) — the change is purely additive
(a new orchestrators/ area + one new isolation kit + registry/README rows).
No existing kit, script, schema, or test is modified, so revert restores the
prior state cleanly with no data or config migration.

Security Impact

  • Adds the agor-daemon-egress security-relevant kit: it widens outbound
    egress to exactly one host:port (host.docker.internal:3030, the Agor
    daemon control-plane). It drops no files, runs no commands, and carries no
    secret. Governed by human review (the kit schema models no governance
    frontmatter); posture documented in the kit README + its ADR.
  • The wrapper never places the USAi key on argv (piped to acq secret set on
    stdin) and refuses to mount a non-Agor-managed local repo checkout,
    avoiding exposure of a user's .env/working files.
  • No secrets, PII, CUI, or internal URLs. CC0 community-hub content; carries no
    compliance authority (references the playbook, does not restate it).

msb backend note (#260 closed)

The earlier "msb is a gap" framing is stale and removed. #260 was filed
because acq's msb adapter mounted the workspace at a fixed guest path and
wired no extra mounts — but quickstart#230 + the #233 parity omnibus changed msb
to mount each workspace at its host path (sbx-parity) with multiple
positional mounts
, so the worktree .git gitdir: pointer resolves the same
on msb. The wrapper is backend-agnostic here; #260 is closed as resolved.
The only residual — a live msb create→exec run on a KVM host (msb is not
live-verified upstream) — is folded into #257. This PR's wrapper header,
agor/README backend table, and the egress-kit spec/README/TROUBLESHOOTING/ADR
were updated to reference #257 instead of the closed #260.

Links

Co-authored-by: OpenCode Agent bret.mogilefsky@gsa.gov

mogul added 10 commits August 2, 2026 05:48
Seed the new integrations/orchestrators/agor/ area with the two source
exploration documents that motivate the Agor + acq isolation integration:

- sandbox-abstraction.md: Agor executor-hook sandbox abstraction design
  (executor_command_template, sbx/msb CLI shapes, mount strategies, the
  SandboxProvider abstraction, phased rollout).
- sandbox-acq-analysis.md: using acq as the sandbox backend — the four
  kits, who-owns-what split, USAi credential flow, SSH-agent forwarding.

Reproduced verbatim from the source material; status: exploration. Not
indexed by generate_index.py (scans only skills/prompts/workflows/agents/
lessons). Refs #258.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit b8de4140941e4b9d24391e2aa06c377d35aaea5e)
Establish integrations/orchestrators/ as a new integration class for tools
that DRIVE a sandbox/isolation tool from the outside (own the agent+session
lifecycle, call acq/sbx/msb), as distinct from isolation kits that acq
APPLIES inside a sandbox. Direction of control decides the area.

- Area ADR 0001 (proposed): rationale, drives-vs-applied boundary rule,
  composition corollary (an orchestrator that needs a kit references one
  under isolation/acq-kits/), mirrors the isolation area ADR.
- orchestrators/README.md: boundary rule + what belongs here + index.
- integrations/README.md: new area in 'What belongs here', layout, and an
  Available-integrations row for orchestrators/agor.

ADR status is 'proposed' pending human confirmation (drafted AFK via the
wayfinder map). Refs #250, #255.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit 408ad4e088ca82b08f48957b0b933f12f5e8c97b)
Add sandbox-wrapper-acq.sh: the Agor executor_command_template target that
runs agor-executor inside an acq sandbox.

- Buffers the stdin JSON payload (mktemp + trap cleanup; payload may carry a
  session JWT so it is always removed on exit).
- Derives mounts from the worktree's own .git with zero daemon calls:
  worktree mode reads the gitdir pointer to mount the main repo's .git;
  clone mode mounts the self-contained clone dir only.
- v1 safety gate: refuses to mount a LOCAL repo's checkout (would expose the
  user's working tree/.env); supports Agor-managed remote repos (~/.agor/*)
  and clone-mode branches only (map #251).
- Applies the daemon-egress kit via --kit (acq has no --net-rule; map #259),
  provisions the per-sandbox  secret on stdin (out-of-band, not vended
  by Agor today; map #252), and pipes the payload to .
- Honors a dry-run PRINCIPLE via AGOR_SANDBOX_DRY_RUN=1 (prints the planned
  acq commands and exits) since the script's job is inherently to mutate and
  Agor always invokes it live; deviation documented inline per
  docs/clean-script-standard.md.

Authored to the clean-script standard (strict mode, quoted expansions, mktemp
+ trap, usage, no curl|sh / eval / secret dumps). bash -n clean; four dry-run
cases (clone, agor-worktree, local-repo refusal, missing cwd) verified by hand.
DRAFT pending human review + live end-to-end validation (map #253, #257).

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit 578b79137929062591fd16b2c322e8cba43a47dc)
Add integrations/orchestrators/agor/README.md: the copy-into-your-project
setup guide for running Agor's executor inside an acq sandbox.

Covers how-it-works (executor_command_template → wrapper → acq exec), the
executor_command_template config, prerequisites, a dry-run-first setup, the
env-var configuration table, the mount strategy (worktree .git derivation +
the v1 local-repo refusal gate and the .git-only-staging alternative),
residual risks, daemon reachability via the agor-daemon-egress kit (no acq
--net-rule), the out-of-band USAi credential flow, a who-owns-what table,
backend support (sbx v1; msb/ppp gaps), and the scope/no-compliance-authority
disclaimer.

Status DRAFT pending human review + live validation (map #254, #257).
Local link targets verified to exist.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit 83b8dd268f14c16ec64a485e8860973a9cd57453)
Add the agor-daemon-egress hybrid/v1 mixin kit: allow-list outbound egress
to the Agor daemon control-plane (host.docker.internal:3030 by default) so an
Agor executor running inside an acq sandbox — via the orchestrators/agor
integration — can connect back to the daemon over WebSocket. acq has no
per-invocation egress flag, so daemon reachability must be a kit's
caps.network.allow (research #248/#259); sbx is default-deny.

- spec.yaml: single caps.network.allow entry, no files/commands/secret.
  sbx preserves host:port; msb strips the port (domain-only) — documented.
- Security-relevant (widens egress): governed by human review + a prose
  'Security posture' note in the README and an ADR, since the hybrid/v1 kit
  schema (additionalProperties:false) models no governance frontmatter (those
  are skill-pattern fields). Human-approved for authoring + categorization.
- Lives under isolation/acq-kits/ (applied INSIDE the sandbox) per the #250
  drives-vs-applied boundary, though its only consumer is orchestrators/agor,
  which references it via AGOR_EGRESS_KIT.
- README (backend parity + security posture), TROUBLESHOOTING, scripts/verify
  (sbx live check), ADR; registry + acq-kits README updated.

validate-kits.py: all 6 kits valid. unsafe-shell scan clean. bash -n clean.
Refs #259.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit 856fbc6b663acff669df735228e81d56140afcbc)
Replace the hardcoded ~/.agor/* managed-root heuristic in the wrapper's v1
local-repo safety gate with Agor's real path model, per source confirmation:
AGOR_DATA_HOME (env) > paths.data_home (config) > AGOR_HOME > ~/.agor. Agor
stores repos/ (bare clones) and worktrees/ under AGOR_DATA_HOME, so a main
repo under that root is Agor-managed (safe to mount); anything else is treated
as a user local repo and refused (would expose .env).

- Wrapper resolves AGOR_DATA_HOME|AGOR_HOME|~/.agor and honors an optional
  colon-separated AGOR_MANAGED_ROOTS allowlist (e.g. an EFS/NFS data_home for
  k8s deploys). Prefix match with a trailing-slash guard.
- Documents that the wrapper cannot read config.yaml, so a config-only
  paths.data_home must be exported as AGOR_DATA_HOME (or added to
  AGOR_MANAGED_ROOTS).
- README: new env-var rows + updated mount-strategy note.

Addresses the B review (Copilot's Agor-source query on worktree storage).
Re-verified: bash -n clean; dry-run cases for AGOR_DATA_HOME match, local-repo
refusal (rc5), and an AGOR_MANAGED_ROOTS EFS root all behave. Refs #251, #253.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit b48b2c4a2a77b9e4f053fcb2096aac4d5d6120a6)
Human owner confirmed the integrations/orchestrators/ area and the
drives-vs-applied boundary rule (review item A). Flip ADR 0001 status
proposed -> accepted.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit 76e16a8d25a6f5c6c03da481d016f84451540178)
…downlint

CI-parity fixes for the new orchestrators area and agor-daemon-egress kit:
- Correct relative-link depths in the orchestrators area ADR (repo-root docs/,
  isolation area ADR, agor explorations) and in the egress kit's security ADR
  (orchestrators/, docs/security-skill-governance.md) — all local links now
  resolve.
- Fix MD028 (blank line inside blockquote) in the egress kit README and the
  orchestrators area ADR.
- Flip orchestrators area ADR 0001 to accepted (review A).

Verified: markdownlint-cli2 0 issues; all local links resolve; validate_repo,
unsafe-shell scan, validate-kits --strict, INDEX --check, and the usai-provider
node tests all pass. Refs #256.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
(cherry picked from commit b4fc7c1a41983050410946b860e5b35ed4bbfee2)
Align the egress kit with main's newer kits (quickstart#235 / #273): declare
the source repo in the optional hybrid/v1 `provenance` block. As a standalone
integration kit (not part of the acq-builtin bundle), it declares `repo` only —
no bundle/kit_names — so it does not affect the built-in bundle cross-check.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
Replace the `[ -f ] && ok || { … }` idiom with an if/else so the fallback
block cannot run when the test passes (shellcheck SC2015). No behavior change.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
…losed upstream

quickstart#230 + the #233 parity omnibus made msb mount each workspace at its
host path (sbx-parity) with multiple positional mounts, so the worktree `.git`
`gitdir:` pointer resolves the same on msb — the wrapper is backend-agnostic
here. The engineering gap #260 described no longer exists (#260 closed); the
only residual is a live msb run on a KVM host, folded into the live-validation
ticket #257.

Update the wrapper header, agor/README backend table, and the egress kit
spec/README/TROUBLESHOOTING/ADR to reference #257 instead of the closed #260.

Co-authored-by: OpenCode Agent <bret.mogilefsky@gsa.gov>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

msb backend support for the Agor+acq wrapper (fixed-guest-path mount gap)

1 participant