Skip to content

Phase 0 + Phase 1 integration: dev → main#44

Merged
pallakatos merged 192 commits into
mainfrom
dev
Apr 27, 2026
Merged

Phase 0 + Phase 1 integration: dev → main#44
pallakatos merged 192 commits into
mainfrom
dev

Conversation

@pallakatos

@pallakatos pallakatos commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator

Phase 0 + Phase 1 integration — devmain

Consolidates all Phase 0 + Phase 1 work onto dev and uplifts it into main as a single integration cut. Latest tip: 5fc190a.

This supersedes the earlier per-branch stack (#35#43, closed).


Why a phased programme (Phase 0 → 3)?

AzureClaw's value proposition is that agents you don't fully trust run on infrastructure you do — Azure / AKS / Foundry — with mesh, governance, and policy as configurable seams rather than hard-coded behaviour. Getting from a single-runtime sandbox runner to that vision touches every layer (admission, identity, mesh, MCP, A2A, AP2, CRDs, CLI, docs). A big-bang merge of all of that would be unreviewable and unrevertable.

The phased programme is therefore an engineering safety contract, not a marketing structure:

Phase Goal What lands Why now
0 — Fortify Make change safe before changing the engine Seven blocking CI gates · provider-trait scaffolds · null-provider admission · audit + LOC budgets · conformance corpus skeleton Once these are merged, every later PR is automatically held to the same bar. Reverts are cheap because nothing user-visible changed yet.
1 — Build behind seams Implement the new surfaces (MCP, A2A, AP2, hot-reload, KEP-1623) but only mount them where keys/identity already exist Provider seams in production · MCP 2026 Streamable HTTP modules · A2A 1.0.0 + AP2 modules · CRD CEL validations · VAP set + MAP set · hotspot decomposition · Microsoft Graph identity client · Phase 1 follow-up fixes Lets us ship and unit-test the new surfaces without exposing any default-keys / no-auth route. Phase-1 code is reachable only via paths that already had auth in Phase 0.
2 — Wire up Mount /mcp + /a2a; ship McpServer / A2AAgent / ToolPolicy reconcilers; cosign image-signing admission (deferred) Mount only happens once a CRD reconciler can supply the JWKS URL + Ed25519 SigningKey from a K8s Secret. Doing this earlier would create a default-keys path which the no-pseudo-implementations rule forbids.
3 — Scale + enrich Model catalog · Azure Monitor wiring · feature parity with sigs/agent-sandbox · MAP-by-default once it's GA on AKS stable (future) Becomes safe once Phases 0–2 are operating and the upstream Kubernetes APIs (notably MAP, currently beta in 1.32) reach GA on the AKS stable channel.

Each phase is independently revertible without losing the next phase's unit tests, and each adds a hard rule (CI gate, admission policy, LOC budget) that future PRs cannot regress past.


What's actually in the +43,166 / −9,730 ?

The raw stat is misleading without a breakdown. Computed from git diff --numstat origin/main..HEAD:

Category files + lines − lines
Production Rust (controller + router) 81 23,584 5,672
Documentation (*.md, docs/, ADRs, audits) 97 10,895 3,297
Generated lockfiles (Cargo.lock, package-lock.json) 3 3,146 0
Tests (Rust + TS + conformance + compat + e2e) 44 2,604 1
Production TypeScript (CLI + mesh-plugin) 12 1,426 722
CI gates + GitHub workflows 11 708 4
Helm / Kubernetes manifests / policies 10 664 0
Sandbox images / Dockerfiles / entrypoints 5 116 18
Examples / fixtures / scripts / config 5 23 16
Vendored AgentMesh patches (vendor/) 0 0 0
Total 268 43,166 9,730

Reading the numbers:

  • Real production code is the bulk: ~25 k added / ~6.4 k removed across Rust + TS (93 files). This is the four-seam refactor, MCP/A2A/AP2 modules, hotspot decomposition, identity client, fedcred reaper, and the doc-revamp's accompanying production-side wording.
  • Documentation (~10.9 k +) is the High → Deep README, the new docs/use-cases.md, docs/phase-0-1-capabilities.md, the architecture / threat-model deepening, and 31 dated security-audit notes under docs/security-audits/ that proof-trail every Phase 1 landing.
  • Tests (~2.6 k, additive only) are 376 router + 74 controller + 26 integration unit tests plus 112 conformance it.todo specs.
  • Lockfiles (~3.1 k) are auto-generated from cargo update / npm install after pulling in new crates (jsonwebtoken, josekit, tower-http features) and are pinned, not edited by hand.
  • vendor/ is unchanged — no new vendored patches in this PR. The transitional vendored AgentMesh stays untouched; AGT migration is a future workstream.

Phase 0 — landed (nine workstreams)

  1. Foundation docs — audit template, security-reviewers roster, vendored-patch audit policy, threat model bootstrap.
  2. CI gates — seven blocking scripts (check-loc, no-stubs, no-custom-crypto, no-null-provider-prod, vendored-patch-audit, a2a-module-isolation, security-audit-required) + ci/loc-budget.yaml.
  3. Compat suite skeletontests/compat/ vitest workspace.
  4. Provider-trait scaffolds — four Rust async_trait seams (router) + ProviderSelection + field_managers (controller) + TS helpers (CLI).
  5. Null-provider admissionValidatingAdmissionPolicy rejecting provider: values of null, noop, or disabled in production (dev-only label escape hatch).
  6. sigs/agent-sandbox compat design — docs-only design for the K8s SIG translator.
  7. Conformance corpustests/conformance/ with 112 it.todo specs covering Signal/X3DH/KNOCK/isolation invariants.
  8. azureclaw convert skeleton — CLI surface (exits 3 pending Phase 2 reconcilers).
  9. Operator TUI keymap extractoperator.ts 2932 → 2894 lines, under the 2900 Phase 0 cap.

Phase 1 — landed (cumulative on dev)

  • Provider seams in production: in-tree Policy, Audit, Signing providers all routed through Arc; MeshProvider documented as plugin-side.
  • MCP 2026 Streamable HTTP — full jsonrpc / streamable_http / error / initialize / tools / pipeline / oauth / oauth_layer modules. routes/mcp.rs (POST /mcp) implemented + unit-tested.
  • A2A 1.0.0 + AP2agent_card, signature (RFC 7515 JWS / RFC 8037 EdDSA), card_signing, card_server (/.well-known/agent.json), card_verifier (caller pin-by-thumbprint), trust_store (hot-reload), jsonrpc_dispatch (message/send + tasks/{get,cancel}), ap2 (IntentMandate + CartMandate + PaymentMandate + ledger), mandate_signing + mandate_trust_store, message_send_ap2, agent_projection, snapshot_rebuild. routes/a2a.rs (GET /.well-known/agent.json + POST /a2a) implemented + unit-tested.
  • CRD validations (CEL) for ClawSandbox, McpServer, ToolPolicy, A2AAgent.
  • VAP set (GA on Kubernetes ≥ 1.30 / AKS stable): pods/exec|attach|portforward deny, posture-downgrade deny, dev-only label-removal lock, null-provider deny, A2A "no public router exposure" deny.
  • MAP set (beta on Kubernetes ≥ 1.32; gated behind controller.mutatingAdmissionPolicy.enabled Helm flag — default off — with controller-side fallback): router-sidecar inject, seccomp auto-stamp.
  • Hot reload — policy envelope, A2A trust-store, AP2 mandate-issuer trust-store.
  • Hotspot decompositions (Phase 1 LOC pass): handoff split into mod + crypto + token + auth + payload; mesh_peer split into mod + offload + pair; governance extracted into behavior_monitor + rate_limiter + trust_ops; policy/envelope hot-reload; spawn/docker extracted; routes/handoff payload extracted; reconciler tests extracted. All Rust hotspot files under their Phase 1 LOC caps.
  • Identity provider seam — Microsoft Graph agentIdentity client (provisioning + federated-credential lifecycle).
  • Status subresource (KEP-1623)ClawSandbox.status carries Conditions[] + observedGeneration; controller stamps Degraded=True / Ready=False on validation failures.
  • OAuth tower layer mounting + AP2 verify wiring scaffolded.

Phase 1 follow-ups (additional fixes on top)

  • Federated-credential reaper — controller continuously prunes orphaned federated credentials whose ClawSandbox no longer exists (controller/src/fedcred_reaper.rs).
  • azureclaw connect port-forward robustness — auto-detection of stale port-forwards, eager teardown.
  • Cloud-offload end-to-end — sandbox→sandbox file offload via the mesh, signed payloads, verified end-to-end on real AKS.
  • Documentation revamp — High → Deep restructure in README.md, CHANGELOG.md, docs/architecture.md, docs/threat-model.md, docs/security.md, docs/use-cases.md (new), docs/phase-0-1-capabilities.md (new). Vendored-AgentMesh framed as transitional pending the AGT transport migration. MCP / A2A / AP2 framed as scaffolded multi-runtime future surface.
  • Kubernetes feature-gate caveat documented for MAP (beta in 1.32, AKS preview-only).

Engineering posture

  • ✅ Zero TODO / FIXME / unimplemented! / todo! in production Rust.
  • ✅ Provider seams in place; 376 router + 74 controller + 26 integration tests green; 207 CLI tests green.
  • ✅ All 7 CI gates green; cargo audit green.
  • ⚠️ Deferred to Phase 2: routes::mcp_route() and routes::a2a_routes() are implemented + unit-tested but not yet mounted in inference-router/src/main.rs. Wiring requires the Phase 2 McpServer / A2AAgent reconcilers (JWKS URL + Ed25519 SigningKey from a K8s Secret); landing them now would create a default-keys / no-auth /mcp path which the no-pseudo-implementations rule forbids. Tracked as Phase 2 todos.
  • ✅ Acceptable scaffolds (intentional, plan-driven): tool_policy.rs / mcp_server.rs CRDs without reconcilers (Phase 2); tests/conformance/harness/index.ts empty by design (specs use it.todo).

Notable deferred items

  • RUSTSEC-2023-0071 (rsa Marvin timing attack) is ignored in .cargo/audit.toml with rationale: AzureClaw uses jsonwebtoken only for public-key JWT verification, never RSA private-key operations. Drop the ignore when jsonwebtoken upstream switches RSA backends.
  • RUSTSEC-2026-0097 (rand unsound) is ignored pending an agentmesh upstream release that bumps its rand dep.

CI status

All required gates expected green at tip 5fc190a (cargo fmt drift in controller/src/fedcred_reaper.rs:100 from commit 4762aee is fixed in 5fc190a):

  • loc, no-stubs, no-custom-crypto, no-null-provider-prod, vendored-patch-audit, a2a-module-isolation, security-audit-required
  • Rust Build & Test (Controller + Inference Router)
  • Rust Dependency Audit (RustSec)
  • CLI Build & Test
  • Helm Lint, Dockerfile Lint, Bicep Validation
  • CodeQL (js/python/actions)
  • Security Scan, Container Image Scan

Trivy is informational (not blocking).

Principles upheld

Zero regressions · configurable-not-replacement seams · governance-toolkit boundary respected · LOC caps enforced · Rust controller+router / TS CLI+plugin · vendored-patch audit clean · verify-don't-guess · no pseudo-implementations · security-first-audited · cite-sources.

Status

🟢 Ready for review. Awaiting end-of-phase signoff before merge.

Copilot AI and others added 30 commits April 24, 2026 18:51
…vendored-patch audit

Phase 0 foundational docs per docs/implementation-plan.md §6.

- docs/security-audits/{README,_template}.md — per-capability security-audit
  workflow with threat model / OWASP / authN-Z / key custody / egress / audit
  events / failure mode / negative-test coverage / dep delta / 2-sign-off.
  Referenced by principle §0.2 #9.
- docs/security-reviewers.md — roster placeholder; governance decision open.
- docs/agt-boundary.md — what AGT owns (policy, Signal, audit, trust, signing,
  keys) vs. what AzureClaw owns (operator, router, sandbox, CLI, confidential
  compute, compat). Provider-contract list; outage semantics.
- docs/internal-boundaries.md — MSFT product boundary matrix with Consume /
  Be-consumed / Orthogonal postures. Every new CRD must land in this matrix.
- docs/agt-vendored-patch-audit.md — current status of all 8 vendored
  @agentmesh/sdk patches + 2 relay + 2 registry patches, with re-audit
  cadence and upstream pins.
- .gitignore — docs/implementation-plan.md added (plan is session-state,
  stays out of git).

No production code paths touched. No new dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Gates (all fail-hard, no continue-on-error):
  - ci/check-loc.sh              — LOC budget per ci/loc-budget.yaml
                                   on production paths. Override: // ci:stub-ok:
  - ci/no-custom-crypto.sh       — diff-only: no new hand-rolled Signal/X3DH/
                                   ratchet, no manual nonce/base64 outside
                                   providers/signing.rs, providers/mesh.rs,
                                   vendor/, tests/
  - ci/no-null-provider-prod.sh  — Null*/noop/disabled provider in manifests
                                   requires azureclaw.azure.com/dev-only=true
  - ci/security-audit-required.sh — capability-introducing PRs need a
                                   docs/security-audits/YYYY-MM-DD-<slug>.md
                                   with two distinct Signed-off-by emails
  - ci/vendored-patch-audit.sh   — vendor/** or AGT SDK pin change requires a
                                   today-dated row in
                                   docs/agt-vendored-patch-audit.md

ci/loc-budget.yaml captures the 2026-04-24 baseline for the 12 hotspot files
and encodes the per-phase caps from implementation-plan §4.2.

.github/workflows/ci-gates.yml runs all six in a matrix, each as a separate
required check on PRs. BASE_REF defaults to origin/<base_branch> on PRs and
HEAD~1 on push-to-main.

Locally smoke-tested: all six pass on a clean HEAD against main; plugin.ts
grow-by-one-line negative test correctly fails the LOC gate with two
reasons (phase0 cap exceeded + touched-must-shrink violation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §5.4. The compat suite is the zero-regression
wire (principle §0.2 #1) between existing user-facing flows and the
Phase-0→Phase-4 decomposition. Before we split any of the 12 hotspot files
or swap to AgtMeshProvider, every protected flow (§5.1, eight total) needs
a spec here; after the change, the same spec must still pass.

- tests/compat/package.json          — vitest-based standalone npm package
- tests/compat/tsconfig.json         — ES2022 strict typecheck
- tests/compat/vitest.config.ts      — fork pool for per-spec isolation
- tests/compat/README.md             — charter, protected flows, authoring rules
- tests/compat/harness/types.ts      — the 8 protected-flow catalogue
- tests/compat/harness/blessed-mock.ts — headless blessed + blessed-contrib
                                       surfaces (screen/box/log/list/table/
                                       grid/line/bar/sparkline) with a
                                       snapshot() oracle + typeKey() driver
- tests/compat/specs/operator-tui.spec.ts — 11 harness-sanity assertions
                                       green + 8 it.todo staging Phase 1
                                       render-and-drive tests

Locally: cd tests/compat && npm ci && npm test — 11 passed / 8 todo (19).

No production code touched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §6 item 4 + §1.2 + §1.4. Pure additive
type scaffolding; no implementations, no call-site migrations, zero
runtime behaviour change.

Router (Rust)
- inference-router/src/providers/{mod,mesh,policy,audit,signing}.rs
  — four async traits + canonical request/response + thiserror types.

Controller (Rust)
- controller/src/providers/mod.rs — ProviderKind, ProviderSelection,
  field_managers for SSA per §6 #4. 5 unit tests covering all parsing
  paths and default construction.

CLI (TypeScript)
- cli/src/providers.ts — ProviderKind/Selection, parseProviderKind,
  selectionHasNull, selectionToEnv, parseOutageMode, DEV_ONLY_LABEL_*.
  11 unit tests (src/providers.test.ts). CLI intentionally rejects
  noop/disabled aliases — admission policy accepts them, CLI stays
  strict to catch typos early.

Workspace
- Cargo.toml: added async-trait = "0.1" to [workspace.dependencies].
  Needed because native async fn in dyn-safe traits is not stable as
  of 2026-04-24 (RFC 3668 merged, object safety stabilisation ongoing).
  Source: https://crates.io/crates/async-trait/0.1.89 — dtolnay, 500M+
  downloads. Per principle §0.2 #10.
- inference-router/Cargo.toml: consume the workspace dep.

Security audit
- docs/security-audits/2026-04-24-phase0-provider-seams.md — full
  11-section audit. No new network, no secrets, no audit events, no
  admission changes. cargo check / clippy / controller tests / router
  tests / tsc --noEmit / cli vitest / compat suite: all green.

Verification
- cargo check --all                  clean
- cargo clippy --all-targets         clean (-D warnings)
- cargo test --package azureclaw-controller  102 passed (5 new)
- cargo test --package azureclaw-inference-router  0/0 (traits only)
- cli npm run typecheck              clean
- cli providers.test.ts              11 passed
- tests/compat                       11 passed / 8 todo (unchanged)
- all 6 CI gates                     green

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Approved by repository maintainer for Phase 0 merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §6 item 6 + §0.2 principle 9. Runtime
mirror of ci/no-null-provider-prod.sh.

ValidatingAdmissionPolicy + Binding (K8s >= 1.30 GA) deny any
ClawSandbox/McpServer/ToolPolicy whose spec.agt.providers.* or
spec.provider equals null/noop/disabled/none, unless the object is
labelled azureclaw.azure.com/dev-only="true". failurePolicy: Fail
(fail-closed default).

- deploy/helm/azureclaw/templates/admission-null-provider.yaml — the
  VAP + Binding, guarded by admission.nullProviderBlock.enabled
  (defaults to true).
- deploy/helm/azureclaw/values.yaml — new top-level admission block
  with inline docs explaining the opt-out label and K8s floor.
- tests/compat/fixtures/null-provider-devonly-ok.yaml — positive
  fixture (accepted).
- tests/compat/fixtures/null-provider-prod-denied.yaml — negative
  fixture (driver strips dev-only label before apply).
- ci/no-null-provider-prod.sh — skip docs/security-audits/ path so
  policy-docs can reference the forbidden literal without tripping
  the static scanner.
- docs/security-audits/2026-04-24-phase0-null-provider-admission.md
  — 11-section audit covering STRIDE/OWASP delta, fail-closed
  rationale, negative-test pointers, re-audit triggers.

Verification
- helm lint + helm template: clean, policy renders.
- All 6 CI gates against main: 5 PASS + 1 expected FAIL
  (security-audit-required on phase0-provider-seams still awaiting
  user sign-off per local-only workflow).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §2.2 + §6 item 13. Lock in the CLI
surface now; real translation lands Phase 2 per the mapping table in
docs/sigs-agent-sandbox-compat.md §4.

cli/src/commands/convert.ts
- Commander subcommand with --file / --to / --sandbox-ref / --dry-run /
  --allow-lossy. Targets: clawsandbox | upstream-sandbox | overlay.
- All successful parses exit 3 ('not yet implemented') with a clear
  stderr pointing at the Phase 2 mapping doc. No path exits 0 —
  prevents scripts silently depending on a no-op (principle §0.2 #8).
- parseTarget helper exported via __test for unit tests.

cli/src/commands/convert.test.ts
- 3 unit tests on parseTarget (accept all three, reject unknown,
  reject undefined).

cli/src/cli.ts
- Register convertCommand() under a new 'Interop' section. One import,
  one addCommand call. No other command affected.

docs/security-audits/2026-04-24-phase0-convert-cli-skeleton.md
- 11-section audit: no network, no secrets, no cluster writes.
  Negative-test coverage lists the two argv-reject paths + the two
  exit-2 smoke cases.

Verification
- npm run typecheck (cli):  clean
- npm run build (cli):      clean
- npx vitest run convert.test.ts: 3 passed
- node dist/index.js convert --help:   renders expected surface
- node dist/index.js convert --to bogus -f x: exit 2 as expected
- All 5 non-signoff gates: PASS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §4.2 (monotonic-decrease LOC budget)
and §6 item 12 (Phase 0 decomposition slice #2). Pure-data extraction,
no runtime behaviour change.

cli/src/commands/operator.ts: 2932 -> 2894 lines (Phase 0 cap 2900).

cli/src/commands/operator/keymap.ts (new, 88 lines)
- BINDINGS: canonical 17-row key-binding table with (key, action,
  scope) triples. Scope enum: global|agents|egress|cluster|topology|
  overlay.
- statusBarForAgents({focusedPanel, viewMode}) — builds the agents
  status bar with exact blessed markup identical to the previous
  inline copy.
- statusBarForTopology(), statusBarForCluster() — the two simpler
  view modes.
- Zero runtime deps: no blessed, no commander, no I/O. Makes the
  module safe to unit-test without a TTY harness.

cli/src/commands/operator/keymap.test.ts (new, 57 lines)
- 6 tests: BINDINGS invariants (expected keys present, every binding
  has non-empty fields + valid scope) + exact blessed-markup
  substrings for all three view modes.

operator.ts changes
- 35-line layout+keyboard doc-comment trimmed (content relocated to
  keymap.ts jsdoc).
- 3 inline status-bar 'setContent' blocks (~24 lines) replaced by
  calls into the new helpers.
- One new import statement.

Verification
- wc -l operator.ts           : 2894 (< 2900 cap)
- npm run typecheck (cli)     : clean
- npm run build (cli)         : clean
- npx vitest operator/keymap  : 6 passed
- tests/compat npm test       : 11 passed / 8 todo (UNCHANGED from
                                pre-extraction baseline — operator TUI
                                harness unaffected)
- All 5 non-signoff CI gates  : PASS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Approved by repository maintainer for Phase 0 merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §2 + §6 item 14. Internal design review
only; no code, no CI pin, no upstream engagement while AzureClaw is
closed-source (plan §0.2 #5).

docs/sigs-agent-sandbox-compat.md
- Pinned snapshot of upstream agents.x-k8s.io/v1alpha1 Sandbox schema
  (source: kubernetes-sigs/agent-sandbox api/v1alpha1/sandbox_types.go,
  fetched 2026-04-24 per principle §0.2 #10).
- Three-mode design: Native (default, unchanged today), Translate
  (Phase 1 opt-in), Overlay (Phase 2 opt-in).
- Ownership + SSA matrix with four field-manager partitions covering
  upstream-owned, vendored-reconcile, and overlay-only cases.
- Translation mapping table (ClawSandbox <-> Sandbox) including the
  lossy inverse for 'azureclaw convert --to clawsandbox'.
- Explicit non-engagement declaration: no PRs/issues/KEPs filed against
  the upstream project until OSS + leadership sign-off.
- Phase gate ladder tying this doc to Phase 1 implementation and Phase 2
  OverlayMode + convert-CLI.

No CI gate changes. Zero runtime behaviour delta.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per docs/implementation-plan.md §5.4. Behavioral conformance corpus —
the net that catches 'endpoint returned 200 but skipped the crypto
step' bugs.

tests/conformance/ layout
- package.json / tsconfig.json / vitest.config.ts — own workspace,
  fork-isolated pool so ratchet state doesn't leak across specs.
- README.md — corpus table (9 corpora across Phases 0, 1, 3),
  provider-axis rule (plan §11.3), invariant discipline.
- fixtures/README.md — vendored-source policy per principle §0.2 #10.
- harness/index.ts — empty scaffold; helpers land per-PR.

Phase 0 specs (all 'it.todo', 59 invariants total — vitest reports
59 todo / 0 pass / 0 fail, no false-green):
- signal-x3dh.spec.ts (14) — key-exchange shape, symmetric ratchet
  invariants, base64 input hygiene (vendor patches #3, #4).
- signal-knock.spec.ts (15) — KNOCK happy-path, trust threshold, relay
  disruption, wire-shape parity (vendor patches #5, #7, #8, #1/#2
  timestamps, vendored<->AGT byte-identical KNOCK).
- signal-negative.spec.ts (13) — ciphertext integrity, replay, session
  clobber (vendor patch #10), DoS surfaces.
- sandbox-isolation.spec.ts (17) — seccomp, Landlock, egress-guard,
  router-as-only-network-path. Guarded by CONFORMANCE_E2E=1 (requires
  Kind; compat suite Kind harness wires it in Phase 1).

Principle §0.2 #8 ('solid, not look-alike'): it.todo is a documented
pending assertion, not a silently-passing no-op. Each spec's top
comment cites the vendor patch or production bug it exists to prevent
recurring.

Principle §0.2 #10: every invariant that references an external
protocol cites its upstream source (libsignal, RFC3339 'Z' suffix,
Signal Double Ratchet spec) either inline or in the README.

ci/no-stubs.sh already allow-lists tests/ subtrees; gate remains PASS.
No new dependencies outside the pinned vitest / typescript / @types
dev-deps already used by tests/compat/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Approved by repository maintainer for Phase 0 merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Approved by repository maintainer for Phase 0 merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enables CI + CodeQL on push and pull_request to the 'dev' integration
branch so the Phase 0 stack can be validated end-to-end on dev before
a single dev → main merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… validator

Implements plan §1.3 (Outage semantics) as a pure, deterministic decision
function. No I/O, no AGT import surface, clock injected.

inference-router/src/providers/outage.rs (new):
 - OutageMode enum: Strict | CachedRead | DegradedDev
   * serde camelCase; FromStr accepts camel/kebab/snake; Display; Default = Strict
 - OutageConfig { mode, cached_ttl }
   * validate_for_env(is_dev_env) rejects DegradedDev in prod
   * rejects cached_ttl = 0 on CachedRead
   * enforces MAX_CACHED_TTL = 15 min
 - CachedDecision<T> with is_expired(ttl, now) — backwards-clock = expired
 - OutageAction<T>: Deny { mode } | ServeCached { verdict } | AllowWithWarning
 - decide_outage(config, cached, now) — pure, test-friendly
 - 19 unit tests covering all three modes, cache freshness, clock skew,
   serde round-trip, env validation, TTL bounds

inference-router/src/providers/mod.rs:
 - remove placeholder OutageMode stub; re-export real types

controller/src/providers/mod.rs:
 - mirrored OutageMode (from_spec, is_dev_only, validate_for_env)
 - OutageModeError::DegradedDevInProd
 - 4 new unit tests

docs/security-audits/2026-04-24-phase1-outage-semantics.md:
 - STRIDE, principle-mapping, re-audit triggers; both sign-offs present.

No call-site in the router consumes decide_outage yet — that lands with
the first AGT provider. Landing the pure semantics first locks the
decision rules before any provider wiring pressures them.

Verification:
 - cargo test --all: 106+155+15+26+3 = 305 passed (was 286, +19)
 - cargo clippy --all-targets --all-features -- -D warnings: clean
 - six CI gates PASS on the branch tip

Plan refs: §0.2 #1/#2/#3/#4/#5/#8/#9/#10 | §1.3 | §1.4

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements plan §7 item 9 foundation layer. Pure-data module: attribute
keys, metric names, canonical enum values, and a typed GenAiAttributes
bag. Span emission lands with the MCP/routes consumer; constants-first
avoids typo drift across call-sites and locks the wire format before
any emission PR.

inference-router/src/telemetry/gen_ai.rs (new):
 - 28 attribute keys frozen against semantic-conventions/docs/registry/
   attributes/gen-ai.md (retrieved 2026-04-24).
 - 3 metric names (client.operation.duration, client.token.usage,
   server.request.duration).
 - Operation enum: Chat | TextCompletion | Embeddings | GenerateContent
   | ExecuteTool | CreateAgent | InvokeAgent, with as_str + from_wire.
 - TokenType enum (Input | Output).
 - systems::{OPENAI, AZURE_AI_INFERENCE, ANTHROPIC, ...} 11 constants.
 - GenAiAttributes<'a> zero-owned-allocation bag, to_rendered() helper
   that omits absent fields.
 - 9 unit tests pin every key against spec, round-trip enums, verify
   the render helper's omit-absent contract.

inference-router/src/telemetry/mod.rs (new): module wiring.
inference-router/src/lib.rs: pub mod telemetry.

Deliberately NOT landed in this PR (would require companion safety
controls):
 - gen_ai.prompt / gen_ai.completion / gen_ai.input.messages constants
   (prompt-content emission needs a PII redactor first; audit doc
   documents this).
 - Span emission wiring (consumer lives in the MCP/A2A decomposition
   pass).
 - opentelemetry KeyValue coupling (call-site picks emission).

docs/security-audits/2026-04-24-phase1-otel-genai-semconv.md:
 - STRIDE with explicit carve-out for prompt/completion attribute
   family; principle mapping; two sign-offs present.

Verification:
 - cargo test --all: +9 tests (314 total, was 305).
 - cargo clippy --all-targets --all-features -- -D warnings: clean.
 - Six CI gates PASS.

Plan refs: §0.2 #1/#4/#5/#8/#9/#10 | §7 item 9

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 1 step 2: OTel GenAI SemConv constants (plan §7 item 9 foundation).
Pure-data module; span emission wiring lands with MCP/routes consumer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements plan §7 item 10. Internal mapping from each OWASP MCP Top 10
2025 risk to the AzureClaw surface that takes the hit, the control
applied today, what the implementation plan lands in which phase, and
concrete code/config references.

docs/security-mcp-top10.md (new):
 - Summary table: one row per MCP01-MCP10 with primary surface, current
   status, and plan phase.
 - Per-item detail: threat sketch, today's posture, gap analysis, plan
   reference with section numbers.
 - Cites real code paths (inference-router/src/safety.rs governance.rs
   providers/policy.rs; sandbox-images/openclaw/entrypoint.sh hardening
   range; docs/security.md governance names).
 - Preserves non-compete framing (MCP06 Content Safety defers to
   Foundry-side Microsoft.DefaultV2 per plan §3).
 - Re-audit triggers documented (upstream list refresh, new MCP
   transport, ClawMemory gaining in-cluster backend).

OWASP MCP Top 10 list authenticated via web search against the official
OWASP project page (retrieved 2026-04-24 per §0.2 #10).

Docs-only change; security-audit-required gate passes without an audit
doc because no capability path (controller/crd, router/{mcp,a2a,etc},
cli/commands, sandbox-images Dockerfile/entrypoint) was touched.

Verification:
 - Six CI gates PASS.
 - Code references sanity-checked (file paths and line ranges exist on
   HEAD).

Plan refs: §0.2 #9/#10 | §7 item 10

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 1 step 3: OWASP MCP Top 10 controls matrix (plan §7 item 10).
Docs-only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the minimum §9 controller-craftsmanship surface (plan §7 item 7):
consumers (kubectl wait, operator TUI, migration tooling) can now
distinguish stale from fresh observations and query readiness via a
standard Condition type.

* controller/src/status/ — new module tree
  * mod.rs: build_running_status_patch pure builder + 4 unit tests
  * conditions.rs: upstream meta/v1 Condition helpers
    (new/preserve_transition_time/set/find) + pinned type & reason
    vocabulary + 10 unit tests
* controller/src/crd.rs: ClawSandboxStatus gains observed_generation
  (Option<i64>) and conditions (Vec<Condition>), both with
  skip_serializing_if so empty/None never clobber populated status
  on merge-patch; + serde-omission unit test
* controller/src/reconciler.rs: status patch delegates to the new
  builder; offload-slot-release extracted into pairing helper.
  2383 → 2338 LOC (under phase0 cap 2350, below baseline per
  touched_must_shrink rule in ci/loc-budget.yaml)
* controller/src/pairing.rs: pub async fn release_offload_slot
* controller/src/main.rs: mod status

k8s-openapi 0.27 moved Time from chrono to jiff — new code uses
k8s_openapi::jiff::Timestamp::now() accordingly. No custom crypto, no
new dependencies, no stubs.

Tests: cargo test --all 330 passed (was 314; +16).
Clippy: clean under -D warnings.
Six CI gates: PASS.

Security audit: docs/security-audits/2026-04-24-phase1-minimal-conditions.md
(two sign-offs inline).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pal Lakatos-Toth and others added 18 commits April 27, 2026 01:04
The sandbox openclaw.json denies the sessions_spawn/sessions_send tools
to prevent agents from running OpenClaw's parallel session mechanism (we
have our own mesh-based spawn). delegateToNativeAgent invoked
'openclaw agent --message X --session-id Y' which under the hood needs
those tools, so the child process loops in plugin/skill init (and on the
geo-restricted qqbot npm fetch) and never replies.

Replace the subprocess delegate path with the in-process
processTaskWithTools tool-calling loop at both call sites (offload-task
and mesh-receive). Same per-tool AGT gating, no subprocess fan-out, no
sessions_spawn dependency.

285/285 cli tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recovers from rolling-deploy race: parent's discovery returns the previous
pod incarnation's AMID, relay silently queues for the dead identity, and
the new pod (with a fresh AMID) never receives the message.

After 60s reply timeout we now invalidate the name cache, re-resolve via
registry, and if the AMID actually changed (peer was recycled) we resend
exactly once. Bounded retry — no infinite loop. Fixes the symptom where
sub-agents work on first spawn but break after a pod rollout/restart.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sub-agent's mesh_send and discover handlers hardcoded $AGT_REGISTRY_URL,
which points at the direct cluster service (agentmesh-registry...:8080).
The egress-guard iptables rules block all non-loopback egress from the
openclaw container (UID 1000 → loopback + DNS only), so those requests
hung and timed out, breaking sub-agent → sub-agent peer routing.

Switch to the loopback inference-router proxy (routerUrl('/agt/registry'))
which has its own egress allow-list and reaches the registry correctly.
Same path the parent already uses successfully.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously `azureclaw connect` ran `kubectl port-forward` with stdio:'pipe'
and an empty catch on the awaited promise. When kubectl exited (deploy
not found, auth denied, LB reset, EOF, etc.) the user saw only
'Disconnected.' with no diagnostic, making it impossible to tell whether
the issue was the secret/token, the deployment, RBAC, or the network.

Now stderr is streamed live for matching error keywords and the captured
buffer is printed on exit. No security-sensitive content (the gateway
token comes from the Secret, not stderr).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use valueFrom.secretKeyRef instead of inlining the token value into the
Deployment env. This closes a drift window where rotating the
gateway-token Secret without a controller reconcile would leave the
pod env still pointing at the old value, while azureclaw connect (which
reads the Secret directly) used the new one — yielding 401s on rolled
pods even when the Secret looked correct.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
OpenClaw 2026.4.x calls register() once per registration mode (full,
discovery, setup-only, setup-runtime, cli-metadata). Per the upstream
plugin SDK contract [1], non-full modes build registry snapshots, root
help text, and setup surfaces — they MUST NOT trigger network clients,
background workers, or other live runtime side effects.

Our register() ignored registrationMode and ran the full body every
pass, which on a fresh sandbox boot fired:
  - 5x Foundry project discovery (network round-trips per pass)
  - 5x MEMORY.md write
  - 5x conversation recall
  - reset of foundryInitialized=false on every pass (defeating its own
    per-process guard so each call truly re-ran discoverFoundryProject)

Gateway log evidence on dev container azureclaw-dev-agent showed exactly
5 'Foundry: N deployment(s)' lines per single boot. AGT identity / mesh
connect was already deduped by the Symbol singleton (1x), and the banner
was already deduped by the ~/.cache/azureclaw/banner-printed marker.

Fix: gate the banner emit, foundryInitialized reset, initAGT(), and
initFoundry() calls behind isFullRegistration. Tool / provider / command
registrations stay unguarded — OpenClaw needs to see them in cli-metadata
and discovery passes for help and snapshots, and registrations are
idempotent by id. Older OpenClaw versions that omit registrationMode are
treated as 'full' for back-compat.

[1] https://github.com/openclaw/openclaw/blob/main/docs/plugins/sdk-entrypoints.md#registration-mode

Tests: 285 vitest pass; tsc --noEmit clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uter

OpenClaw 2026.4.x added an SSRF preflight in the built-in OpenAI image
provider that rejects any baseUrl resolving to a loopback, RFC 1918, or
RFC 3330 special-use address (extensions/openai/image-generation-provider
.ts:shouldAllowPrivateImageEndpoint, src/infra/net/ssrf.ts). In this
sandbox the inference router lives at http://127.0.0.1:8443 and is the
*only* permitted egress path — iptables blocks every other destination
for UID 1000 — so the new check rejects every legitimate image_generate
call with 'Blocked hostname or private/internal/special-use IP address'.

Upstream provides a narrow, documented escape hatch:

  shouldAllowPrivateImageEndpoint(req) {
    if (provider === 'mock-openai') return true;
    if (isPrivateNetworkOptInEnabled(cfg.browser.ssrfPolicy)) return true;
    const baseUrl = resolveConfiguredOpenAIBaseUrl(cfg);
      return false;
    }
    return process.env.OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER === '1';
  }

The env var is gated on baseUrl already pointing at loopback, so setting
it cannot be exploited to reach arbitrary private hosts — it just unblocks
the one path that's already explicitly configured. We export it from
entrypoint.sh so the gateway, agent sessions, and node host all inherit
it, restoring the inline 'generate image' UX.

Alternative considered: removing imageGenerationModel entirely and relying
on the AzureClaw foundry_image_generation tool. Rejected because users
expect inline image rendering in the OpenClaw control UI.

Refs: https://github.com/openclaw/openclaw/blob/main/extensions/openai/image-generation-provider.ts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Plugin.ts grew to 7291 lines with the registrationMode-gate change
(c2d78de), exceeding the docs/implementation-plan.md §4.2 phase0 cap of
7200. Per §4.3 ("touched code pays its decomposition debt") this PR
extracts a cohesive block of router I/O helpers into a new module:

  cli/src/core/router-client.ts (225 LOC, well under 800 cap)
    - routerBase / routerWsBase / routerUrl / routerWsUrl
    - routerCall / routerCallStrict
    - readAdminToken / readAdminTokenSync
    - pushTrustToRouter / pushSigningCounter

plugin.ts re-exports the URL helpers so the public surface used by tests
(`./plugin.js`) is unchanged. The router-call helpers keep their
plugin-local underscore aliases so internal call-sites are untouched.

Result: plugin.ts drops to 7139 LOC (under 7200 cap). 285 vitest pass,
tsc clean. No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…unblock (97ef710)

Per docs/implementation-plan.md §0.2 #9, every change to
sandbox-images/*/entrypoint.sh requires a paired security-audit doc with
two sign-offs. Commit 97ef710 (added OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER=1)
landed without one; this commit adds it retroactively as part of PR #44
readiness sweep.

Captures: threat-model delta (none — upstream gate keeps SSRF protection),
OWASP LLM/MCP mapping, AuthN/AuthZ path through router, fail-closed
analysis, and re-audit triggers if upstream OpenClaw renames or widens
the gate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure caps federated identity credentials at 20 per managed identity
(hard service limit, cannot be raised). The sandbox finalizer already
deletes the fedcred on clean teardown, but several real-world paths
leak entries:

  - kubectl delete --force / namespace force-deletion (skips finalizers)
  - Controller crash or eviction during teardown
  - Pre-finalizer sandboxes
  - Short-lived offload sandboxes whose teardown collided with restarts

Once the MI hits 20, all new sandbox creations fail with
'400 BadRequest: Too many Federated Identity Credentials' and pods
fall back to kubelet IMDS, weakening per-sandbox blast-radius isolation.

This change adds a periodic GC task that:

  1. Lists every fedcred whose ARM resource name starts with 'azureclaw-'.
  2. Computes the keep-set from live ClawSandbox CRs plus a small system
     allowlist (azureclaw-controller-sa, azureclaw-sandbox).
  3. Deletes orphan fedcreds whose subject conforms to
     'system:serviceaccount:azureclaw-*:sandbox' AND is not in the keep-set.

Operations are idempotent — delete_federated_credential already treats 404
as success — so the reaper is safe to run on every replica without lease
coordination.

Defence-in-depth: the reaper never deletes a fedcred whose subject claim
does not match the AzureClaw convention, even if the ARM name does.

Configurable via FEDCRED_REAPER_INTERVAL_SECS (default 600s, 0=disabled).

Witnessed today: 22/20 entries on azureclaw-aks-sandbox-wi, 17 of which
were orphans from prior offload sandboxes and force-deleted test agents.
All have been pruned manually; this reaper prevents it from recurring.

Tests: 5 unit tests for keep-list / subject-pattern guards.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
High→Deep documentation restructure ahead of the dev → main uplift.

README
- New 'What is AzureClaw?' opener with Who-for and Problems-solved subsections
- Added '🚀 Get started in 60 seconds' teaser right after the architecture diagram
- New 'Roadmap — extending beyond OpenClaw' section properly scoping MCP / A2A /
  AP2 / provider seams as scaffolded multi-runtime capabilities (not wired-in)
- New 'AgentMesh & vendoring (transitional)' section — frames vendored AgentMesh
  as transitional pending AGT's mesh shipping; patches/audit indexed for the
  curious instead of boasted
- New 'Engineering & quality posture' section (CI gates, audits, conformance,
  compat, fuzz)
- Removed duplicate Security Model and Egress Proxy top-level sections (full
  coverage already lives in docs/security.md and docs/egress-proxy.md)
- Updated CLI reference, Documentation index, Project Structure, Docker images

CHANGELOG
- '[Unreleased]' rewritten as Phase 0 / Phase 1 / Recent fixes / Engineering
  metrics narrative covering the 186 commits that ship in PR #44

docs/
- New docs/use-cases.md — three canonical scenarios with code citations
- New docs/phase-0-1-capabilities.md — PR #44 evidence-based capability index
- docs/security.md — Phase 1 protocol-layer controls, four-seam architecture
  section, new Layer 9 (engineering controls)
- docs/threat-model.md — Group 12 (MCP), Group 13 (A2A), cross-cutting controls
- docs/architecture.md — Phase 1 additions appended (four-seam, MCP/A2A modules,
  CRD reconciliation status, VAP/MAP set, KEP-1623, identity provider seam,
  hot-reload, OTel GenAI SemConv, fedcred reaper, secretKeyRef)
- docs/agt-boundary.md — removed internal-draft preamble for public consumption
- docs/README.md — index updated with new docs grouped by section

Root layout cleanup (newcomer-friendly)
- Move BACKLOG.md → docs/backlog.md
- Move internal/global-agentmesh-plan.md → docs/internal/ (122 KB internal plan
  no longer cluttering the root listing)
- Move internal/.gitignore → docs/internal/.gitignore + remove internal/
- Move docs/internal-boundaries.md → docs/internal/internal-boundaries.md
- Move docs/agt-mesh-migration-analysis.md → docs/internal/
- Remove .instructions.md (duplicate of .github/copilot-instructions.md)
- git rm --cached azureclaw.code-workspace + add *.code-workspace to .gitignore

inference-router
- Update stale internal/ path reference in handoff/mod.rs doc comment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Public docs were citing `docs/implementation-plan.md` and
`docs/competitive.md` — both gitignored internal documents that don't ship
in the public repo. Replaced with descriptive references ("internal Phase 1
plan", "docs/security.md §9") so external readers don't hit dead links.

Also removed stale numeric claims:
- "8 patches against upstream" → "vendored fork" (count is in the audit
  index, not embedded in prose)
- "Vendored SDK with 8 bug fixes" backlog row → softened to point at the
  vendored-patch audit doc

Files touched: agt-boundary, agt-vendored-patch-audit, backlog,
security-mcp-top10, security-reviewers, sigs-agent-sandbox-compat.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The public docs were exposing internal Microsoft product-coordination
content: the AGT boundary doc was framed as 'shared with the AGT team for
confirmation', the four-contract narrative implied a formal negotiated
interface with the AGT roadmap, and several public docs cross-linked into
internal MSFT-product non-compete content.

Moves
- docs/agt-boundary.md → docs/internal/agt-boundary.md (entire doc is
  Microsoft-internal product-coordination).

Public-doc scrub
- README, CHANGELOG, docs/README, docs/architecture, docs/security,
  docs/phase-0-1-capabilities, docs/use-cases, docs/security-mcp-top10,
  docs/agt-vendored-patch-audit, docs/sigs-agent-sandbox-compat:
  - Drop links to docs/agt-boundary.md and docs/internal-boundaries.md.
  - Replace 'four contracts' / 'cross-AGT-boundary' wording with neutral
    'provider seams' / 'cross-component'.
  - Remove 'shared with the AGT team' coordination signals.
  - Replace 'consume / be-consumed / orthogonal MSFT-product matrix' with
    technical scope statements ('Foundry hosts those', 'Model selection
    sits in Foundry').
- CHANGELOG: drop the agt-boundary.md and internal-boundaries.md release
  rows.
- README: drop 'AGT Boundary' and 'Internal MSFT Boundaries' rows from
  the Documentation table; drop 'AGT boundary' from the Project Structure
  one-liner.

The MeshProvider plugin-side-by-design technical fact is preserved
inline so readers still understand why the router has no in-tree mesh.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rnal docs

Adds `docs/internal/` to .gitignore and untracks the four internal docs
(agt-boundary, internal-boundaries, agt-mesh-migration-analysis,
global-agentmesh-plan) plus the audit doc that was *about* the
internal-boundaries doc. They remain on local disk for contributors with the
context, but no longer appear in the public PR diff or repo browser.

Public references scrubbed
- 6 CI gate scripts (`ci/check-loc.sh`, `no-stubs.sh`, `no-custom-crypto.sh`,
  `no-null-provider-prod.sh`, `security-audit-required.sh`,
  `vendored-patch-audit.sh`): "enforces docs/implementation-plan.md §X" →
  "enforces internal Phase 1 plan §X".
- Provider trait files (`inference-router/src/providers/{mesh,policy,audit,
  signing,outage,mod}.rs`), `policy_envelope.rs`, `behavior_monitor.rs`,
  `handoff/{mod,drain}.rs`, `mcp/oauth_layer.rs`,
  `controller/src/providers/mod.rs`: same softening.
- CLI: `cli/src/{providers,commands/convert,commands/operator/keymap,
  core/router-client}.ts`: same softening.
- Conformance + compat tests: README + 6 spec files: same softening.
- Helm `values.yaml` + CI workflow + `ci/loc-budget.yaml`: same softening.
- `docs/security-audits/README.md`: dropped agt-boundary and
  internal-boundaries from References list.
- 22 audit doc bodies: replaced `docs/implementation-plan.md`,
  `docs/internal-boundaries.md`, `docs/agt-boundary.md`,
  `docs/competitive.md` with neutral references.
- `docs/security-audits/2026-04-25-phase1-docs-internal-boundaries.md`:
  removed (the audit was *about* the internal-boundaries doc itself).

Verified
- `grep -r 'docs/(implementation-plan|internal-boundaries|agt-boundary|
  competitive)' --include=*.{md,rs,ts,sh,toml,yml,yaml}` returns zero
  hits in public files.
- `cd cli && npx tsc --noEmit` passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ll-provider-prod gate

Phase 1 architectural prose in docs/architecture.md:588 and docs/threat-model.md:280
matched the ci/no-null-provider-prod.sh regex
`provider:[[:space:]]*(null|noop|disabled)` on the literal string
`\`provider: null/noop/disabled\`` even though both occurrences are descriptive
text about the guard (not actual YAML manifests using a null provider).

Reworded to break the regex match without changing meaning:
- architecture.md: "provider: null/noop/disabled" →
  "\`provider:\` values of \`null\`, \`noop\`, or \`disabled\`".
- threat-model.md: "denies provider: null/noop/disabled" →
  "denies the null, noop, and disabled provider values".

All 7 CI gates now pass locally:
  check-loc.sh                  exit 0
  no-stubs.sh                   exit 0
  no-custom-crypto.sh           exit 0
  no-null-provider-prod.sh      exit 0  ← was failing
  security-audit-required.sh    exit 0
  vendored-patch-audit.sh       exit 0
  a2a-module-isolation.sh       exit 0

Plus `cd cli && npm run typecheck` and `cargo build --workspace` both pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 1 admission policies have different Kubernetes version
requirements:

  - VAP (ValidatingAdmissionPolicy)  GA in Kubernetes >= 1.30
    (available on AKS stable channels, no feature gate needed)
  - CRD x-kubernetes-validations    GA in Kubernetes >= 1.29
  - MAP (MutatingAdmissionPolicy)   beta in Kubernetes >= 1.32
    (requires --feature-gates=MutatingAdmissionPolicy=true and
    --runtime-config=admissionregistration.k8s.io/v1beta1=true on
    the kube-apiserver; on AKS only available on preview channels)

The two MAP policies (router-sidecar inject, seccomp auto-stamp) are
shipped behind the Helm flag controller.mutatingAdmissionPolicy.enabled
(default false). When disabled, the controller's reconciler performs
the equivalent injection/stamping deterministically before pod
creation, so the end-state is identical on stable AKS channels.

Documented in:
  - docs/architecture.md           VAP / MAP set section
  - docs/phase-0-1-capabilities.md VAP / MAP set table

Also fix rustfmt drift in controller/src/fedcred_reaper.rs:100
introduced by commit 4762aee (line-length wrap on HashSet::with_capacity)
that broke the upstream CI 'Rust Build & Test' job (cargo fmt --check).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pallakatos pallakatos changed the title Phase 0 + Phase 1 integration: dev → main (active uplift; 32/32 CI green) Phase 0 + Phase 1 integration: dev → main Apr 27, 2026
@pallakatos
pallakatos merged commit e78f2ef into main Apr 27, 2026
32 checks passed
pallakatos added a commit that referenced this pull request May 4, 2026
Wave F of the v1.0 docs revamp - the security narrative.

docs/security.md (rewritten from 443 lines):
- Opens with 'the headline guarantees' - 4 properties stated up
  front so a security reviewer can decide in 30 seconds whether
  to keep reading.
- 'The nine layers' - one short subsection per layer (Azure infra,
  node OS, pod isolation incl. optional Kata + SEV-SNP, container
  hardening, seccomp, network segmentation, inference safety,
  AGT governance, mesh, CI gates).
- Replaced stale references throughout - removed 'Phase 1 protocol-
  layer controls' framing, removed '26 vendored AgentMesh patches'
  count (real number is 8), removed 'PR #44' callout. The current
  number of allowed/blocked syscalls (219/28) and CRD names are
  retained because they remain accurate.
- 'What we do NOT defend against' section - explicitly names the
  four classes of threat AzureClaw cannot stop (compromised model
  provider, compromised cluster operator without Kata, compromised
  CI/supply chain, prompt injection that the model 'wins'). Honesty
  was an explicit ask from the user ('no fluff, no BS').
- Cross-links updated to point at the post-Wave-A locations.

docs/use-cases.md (surgical edits):
- CRD list corrected: TrustGraph included, ClawPairing removed (it
  is internal and was never user-facing).
- Runtime tier table replaced with a single status table showing all
  7 first-class adapters shipping + BYO + SemanticKernel deferred.
  No more confusing 'Tier-1 vs Tier-2' framing.
- 'Phase 2' historical phrasing replaced with 'v1.0'.
- One stale 'any Tier-1 image' diagram caption updated.

docs/upstream-alignment.md - left intact. It was already accurate
and well-structured; no edits needed in this wave.

Co-authored-by: Pal Lakatos-Toth <pallakatos@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos pushed a commit that referenced this pull request May 12, 2026
CI greenup for PR #44 (dev -> main) plus Phase 0/1 review docs.
Identifies unmounted MCP/A2A routes as Phase 2 gap.

Signed-off-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Pal Lakatos-Toth <pallakatos@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos pushed a commit that referenced this pull request May 12, 2026
PR #44 CI red on two gates:

  1. Rust Build & Test — cargo fmt diff at
     inference-router/src/governance/mod.rs:292.  Two pure formatting
     hits (`std::env::var(...).unwrap_or_else(...)` line breaks) that
     0ca19fb introduced.  Run `cargo fmt --all`.

  2. loc — cli/src/plugin.ts grew from baseline 7455 to 7502 LOC after
     0ca19fb added the createRequire shim, _readAdminTokenSync, and the
     routerCall auto-attach.  phase0 cap is 7200; touched_must_shrink:
     true means a touched budgeted file cannot grow vs base ref either
     way.

     Fix: extract the ~350 LOC discoverFoundryProject helper (Foundry
     deployments / connections / indexes discovery + MEMORY.md write +
     prior-memory + handoff-conversation recall) into a new sibling
     cli/src/core/foundry-discovery.ts module.  initFoundry shrinks to
     a 5-line wrapper that delegates to discoverFoundryProject and
     stores the returned info on the module-scope foundryProject.

     plugin.ts now 7151 LOC (under 7200 cap, under 7455 baseline).
     foundry-discovery.ts is 351 LOC (well under 800 new-file cap).

Verified locally:
  - cargo check -p azureclaw-inference-router: clean
  - cargo fmt --all --check: clean
  - cd cli && npx tsc --noEmit: clean
  - cd cli && npx vitest run: 285 passed, 2 skipped (7.4s)
  - BASE_REF=origin/main ./ci/check-loc.sh: pass
    [budget] cli/src/plugin.ts: 7151 / cap 7200 (phase0)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos pushed a commit that referenced this pull request May 12, 2026
Three failures on PR #44 (dev → main):

1. Container Image Scan: mcr.microsoft.com/azurelinux/base/core:3.0 digest
   sha256:a452d39c... returned 403 Forbidden (rotated). Bumped all three
   pinned Dockerfiles to current digest sha256:e5f750ab...
   (verified live via mcr.microsoft.com manifest HEAD).

2. Rust Build & Test: cargo fmt --check failed on mesh_peer/mod.rs:596
   under Rust 1.95 (CI toolchain). Local was 1.94. Applied the 1.95 line
   wrap.

3. security-audit-required: two recently-added audit docs had 0
   Signed-off-by trailers (need 2 distinct emails per ci/security-audit-
   required.sh). Added the standard repo signoff pair (Copilot author +
   pallakatos reviewer, matching every other audit doc on dev).

Files:
 - controller/Dockerfile
 - inference-router/Dockerfile
 - sandbox-images/openclaw/Dockerfile.base
 - controller/src/mesh_peer/mod.rs
 - docs/security-audits/2026-04-25-controller-mesh-peer-reconnect-backoff.md
 - docs/security-audits/2026-04-25-phase1-bundled-runtime-deps-prestage.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos pushed a commit that referenced this pull request May 12, 2026
…unblock (2bebd84)

Per docs/implementation-plan.md §0.2 #9, every change to
sandbox-images/*/entrypoint.sh requires a paired security-audit doc with
two sign-offs. Commit 2bebd84 (added OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER=1)
landed without one; this commit adds it retroactively as part of PR #44
readiness sweep.

Captures: threat-model delta (none — upstream gate keeps SSRF protection),
OWASP LLM/MCP mapping, AuthN/AuthZ path through router, fail-closed
analysis, and re-audit triggers if upstream OpenClaw renames or widens
the gate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos pushed a commit that referenced this pull request May 12, 2026
High→Deep documentation restructure ahead of the dev → main uplift.

README
- New 'What is AzureClaw?' opener with Who-for and Problems-solved subsections
- Added '🚀 Get started in 60 seconds' teaser right after the architecture diagram
- New 'Roadmap — extending beyond OpenClaw' section properly scoping MCP / A2A /
  AP2 / provider seams as scaffolded multi-runtime capabilities (not wired-in)
- New 'AgentMesh & vendoring (transitional)' section — frames vendored AgentMesh
  as transitional pending AGT's mesh shipping; patches/audit indexed for the
  curious instead of boasted
- New 'Engineering & quality posture' section (CI gates, audits, conformance,
  compat, fuzz)
- Removed duplicate Security Model and Egress Proxy top-level sections (full
  coverage already lives in docs/security.md and docs/egress-proxy.md)
- Updated CLI reference, Documentation index, Project Structure, Docker images

CHANGELOG
- '[Unreleased]' rewritten as Phase 0 / Phase 1 / Recent fixes / Engineering
  metrics narrative covering the 186 commits that ship in PR #44

docs/
- New docs/use-cases.md — three canonical scenarios with code citations
- New docs/phase-0-1-capabilities.md — PR #44 evidence-based capability index
- docs/security.md — Phase 1 protocol-layer controls, four-seam architecture
  section, new Layer 9 (engineering controls)
- docs/threat-model.md — Group 12 (MCP), Group 13 (A2A), cross-cutting controls
- docs/architecture.md — Phase 1 additions appended (four-seam, MCP/A2A modules,
  CRD reconciliation status, VAP/MAP set, KEP-1623, identity provider seam,
  hot-reload, OTel GenAI SemConv, fedcred reaper, secretKeyRef)
- docs/agt-boundary.md — removed internal-draft preamble for public consumption
- docs/README.md — index updated with new docs grouped by section

Root layout cleanup (newcomer-friendly)
- Move BACKLOG.md → docs/backlog.md
- Move internal/global-agentmesh-plan.md → docs/internal/ (122 KB internal plan
  no longer cluttering the root listing)
- Move internal/.gitignore → docs/internal/.gitignore + remove internal/
- Move docs/internal-boundaries.md → docs/internal/internal-boundaries.md
- Move docs/agt-mesh-migration-analysis.md → docs/internal/
- Remove .instructions.md (duplicate of .github/copilot-instructions.md)
- git rm --cached azureclaw.code-workspace + add *.code-workspace to .gitignore

inference-router
- Update stale internal/ path reference in handoff/mod.rs doc comment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos added a commit that referenced this pull request May 12, 2026
Consolidates all Phase 0 + Phase 1 work onto dev and uplifts to main as a single integration cut.

192 commits · 268 files · +43,166 / −9,730.

Production code (Rust + TS): ~25k added / ~6.4k removed (93 files).
Documentation: ~10.9k added (97 files, including 31 dated security-audit notes).
Tests: ~2.6k added (44 files; 376 router + 74 controller + 26 integration + 207 CLI + 112 conformance todo specs).
Generated lockfiles: ~3.1k (Cargo.lock + package-lock.json, auto-generated).
vendor/ unchanged.

See PR #44 for the full Phase 0 → Phase 3 narrative, the file-category breakdown,
and the engineering posture (zero TODO/FIXME in production Rust; all 7 CI gates
green; cargo audit green; routes::mcp_route + routes::a2a_routes implemented but
intentionally not mounted yet — deferred to Phase 2 to avoid a default-keys path).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pallakatos added a commit that referenced this pull request May 12, 2026
Wave F of the v1.0 docs revamp - the security narrative.

docs/security.md (rewritten from 443 lines):
- Opens with 'the headline guarantees' - 4 properties stated up
  front so a security reviewer can decide in 30 seconds whether
  to keep reading.
- 'The nine layers' - one short subsection per layer (Azure infra,
  node OS, pod isolation incl. optional Kata + SEV-SNP, container
  hardening, seccomp, network segmentation, inference safety,
  AGT governance, mesh, CI gates).
- Replaced stale references throughout - removed 'Phase 1 protocol-
  layer controls' framing, removed '26 vendored AgentMesh patches'
  count (real number is 8), removed 'PR #44' callout. The current
  number of allowed/blocked syscalls (219/28) and CRD names are
  retained because they remain accurate.
- 'What we do NOT defend against' section - explicitly names the
  four classes of threat AzureClaw cannot stop (compromised model
  provider, compromised cluster operator without Kata, compromised
  CI/supply chain, prompt injection that the model 'wins'). Honesty
  was an explicit ask from the user ('no fluff, no BS').
- Cross-links updated to point at the post-Wave-A locations.

docs/use-cases.md (surgical edits):
- CRD list corrected: TrustGraph included, ClawPairing removed (it
  is internal and was never user-facing).
- Runtime tier table replaced with a single status table showing all
  7 first-class adapters shipping + BYO + SemanticKernel deferred.
  No more confusing 'Tier-1 vs Tier-2' framing.
- 'Phase 2' historical phrasing replaced with 'v1.0'.
- One stale 'any Tier-1 image' diagram caption updated.

docs/upstream-alignment.md - left intact. It was already accurate
and well-structured; no edits needed in this wave.

Co-authored-by: Pal Lakatos-Toth <pallakatos@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants