Repository navigation
feat: end-to-end ANS identity enforcement via a commerce (AP2) egress capability - #11
Open
aciobanu-godaddy wants to merge 14 commits into
Open
aciobanu-godaddy wants to merge 14 commits into
aciobanu-godaddy wants to merge 14 commits into
Conversation
Self-signing signer + self-signed artifact source selected by dev_mode config, deduplicated COSE minting with selfsign coverage. The rogue is stamped at the egress but rejected only at the ingress. Squashes: a904935 5cbb964 d8d3e03 34214fc 16aa51a Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
…pt toggle Seed+refresh root keys from the transparency log, configurable SCITT receipt enforcement, forward verified requests to the seller, reject oversize bodies (400) before verification, and the ans-reference-callee flags that drive it. Squashes: 416a3d8 5256e52 51f571d bb41bbf 38681b4 f4558a9 Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
P-256 commerce DPoP key (JWK + RFC 7638 jkt), byte-exact RFC 9449 DPoP minter, mandate type with ans_host version-stripping + validation, single-slot mandate cache with skew + injectable clock, authority request_mandate JSON-RPC client, and the rewrite_book_flight orchestrator. The middleware rewrites the body THEN signs the rewritten bytes (R1), behind a commerce gate/config with demo prefetch. Squashes: 3dd638d c4cc4a8 c594177 8a5f72a 5df9704 7a913c0 7704ade cf02479 978635d 404c9fe 7f6df35 Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
…hint Replace the hardcoded commerce branch with a capability.Transform chain driven by a Discoverer (A2A agent-card JWS + MCP tools/list); commerce becomes one transform with a lenient static fallback. Discovery hardened: tool-gated probing, verified-htu only, hermetic tests. HttpResponsePreReturn stage surfaces a seller capability rejection as a machine-readable middleware_hint. Squashes: 29ffc2b 36cc56e 5ed69a7 c02c3f1 293c1ae 36a593b 1120c1c ff1e05b 9789157 4833761 546fc61 ea8f1d1 057afaf Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
…eanup Forward the authority's mandate bytes verbatim (avoid float-format divergence), bind the identity-proof htu to the transform's external (seller) URL (G1), drop deprecated elliptic.Marshal + unify kid computation, neutralize a doc-comment example. Squashes: 57d5338 b604a91 7428387 c42823e Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
…mode run-demo.sh drives the live two-agent OpenShell demo end-to-end from a single .env (no values/secrets in the repo; renders configs at runtime). --narrate adds a trust-boundary diagram and a step-by-step walkthrough with the REAL decoded evidence — cert chain, transparency-log vouch, scoped mandate, DPoP proof claims, ticket, and the rogue rejection; --report writes a markdown copy. The egress surfaces the attached mandate/proof and logs their decoded form at DEBUG only. README/RUNBOOK/SECRETS document setup and the no-secret-leak guarantees. Squashes: 875c19c 5cb5284 6af259c 8bbdc70 6fb2df9 fc78aaf ba47018 Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
aciobanu-godaddy
force-pushed
the
feat/ans-identity-commerce-egress
branch
from
October 27, 2026 07:24
1603f6e to
57dd64a
Compare
Under the CI toolchain's gosec, the discovery + authority client.Do calls (operator/policy-scoped URLs, not request input) are flagged G704, while the duplicate directive on callee.go's NewRequestWithContext line is unused (the real sink client.Do is already suppressed). Add the three needed suppressions and drop the redundant one. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
aciobanu-godaddy
force-pushed
the
feat/ans-identity-commerce-egress
branch
from
October 27, 2026 07:35
6e05160 to
348d249
Compare
Pull the generic middleware assembly (identity, caller auth, vetting, gRPC server, readiness, serve loop) out of cmd/ans-openshell-middleware into a new exported app package: app.Run(ctx, cfg, logger, ...Option). A WithCustomize option receives the built *middleware.Service so a caller can register capability transforms + a discoverer before serving. The exported location (not internal/) lets a separate module (the forthcoming demo) reuse it. The production main shrinks to flag parsing + config load + app.Run; commerce is wired via WithCustomize for now (it moves to the demo module in a later commit). Server-assembly tests move to app/app_test.go. No behavior change. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
Replace Outcome.MandateBytes/DPoPProof (commerce-named) with a generic Artifacts map[string]any the core treats as opaque, plus an optional Narratable interface a transform may implement to render its artifacts at debug. service.go no longer imports internal/commerce: it just calls Narratable if the transform implements it. The commerce adapter now stashes its mandate/proof into Artifacts and implements Narrate (the decoding moved out of the core). Extracted a narrate() helper to keep evaluate() under the cognitive-complexity gate. No behavior change. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
Commerce (AP2 mandate + DPoP) is a demo vertical, not generic middleware, so
it moves out of the core module entirely into a new in-repo module
'tutorial' (github.com/agentnameservice/ans-openshell/tutorial):
internal/commerce -> tutorial/commerce
internal/commercekey -> tutorial/commercekey
internal/middleware/commerce.go (the capability.Transform adapter)
-> tutorial/transform (package transform)
The production binary + core packages are now commerce-free (verified: no
internal/commerce references outside tutorial/). The prod main is pure
generic middleware over app.Run. Two service-level commerce E2E tests were
dropped from internal/middleware (their coverage is retained by
tutorial/transform tests + the stub-based evaluate tests + the live demo);
service_test.go no longer imports commerce.
Module wiring uses a git-ignored go.work (harness/CI generate it); no
replace directive in go.mod. Named 'tutorial' to avoid colliding with the
existing examples/demo and the make-demo binary.
Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
The production binary (cmd/ans-openshell-middleware) now links zero commerce code. The commerce vertical lives entirely in the tutorial module, assembled by its own dev-only binary (tutorial/cmd/ans-demo-middleware) that reuses the generic app.Run and registers the commerce transform + discoverer via an option. - tutorial/cmd/ans-demo-middleware: the demo binary (commerce customizer, mandate prefetch, DPoP clock/nonce), built via the go.work workspace. - tutorial/config: the Commerce config block (moved out of internal/config); the core config no longer parses or knows about commerce. - tutorial/deploy/examples_test.go: the demo example-config test (moved from deploy/, asserts tutorialcfg.Commerce); deployment_test.go stays in core. - internal/middleware/service_test.go: drop helpers left unused after the M3 commerce E2E test removal. Verified: GOWORK=off go list -deps ./cmd/ans-openshell-middleware | grep -c commerce = 0; both modules build and test clean; lint shows only the known CI-authoritative gosec-directive set. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
Wire the two-module layout into the build harness, CI, and docs. - Makefile: 'workspace' (re)generates the gitignored go.work with a version-pinned replace; 'demo-build' builds bin/ans-demo-middleware via it. The production 'build' target is unchanged and needs no workspace. - run-demo.sh: MW_BIN now defaults to bin/ans-demo-middleware (the demo egress is the production middleware plus the commerce tutorial transform); the preflight hint points at 'make demo-build'. - ci.yml: a new 'tutorial' job generates the workspace (not committed) and runs build, demo-binary build, race tests, and lint for ./tutorial/...; the core build-and-test and lint jobs stay core-only (no go.work in a fresh checkout). - README: a Repository layout section documenting the core/tutorial split and that the shipped binary contains no commerce code; live-demo README/RUNBOOK build the demo binary and run it as the egress. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
…, docs Follow-up to the demo/core separation, from a self-review of PR #11: - cmd/ans-openshell-middleware: fix the stale 'demo/cmd/...' doc path (renamed to tutorial/cmd/... during extraction) and document that the production binary strict-rejects a config carrying a commerce: block, since app.Config has no commerce field and the loader is strict. - tutorial/transform: replace the raw capability.OutcomeKind(outcome.Kind) int conversion with an explicit capabilityKind switch so the commerce and capability enums are not assumed to share integer values; an unknown kind fails closed to Deny. Add TestCapabilityKind to pin the mapping. - tutorial/config: add an exported FileConfig (app.Config inline + Commerce) used by both the demo binary and the example-config test, so the test exercises the real load path instead of a hand-maintained mirror struct that could silently drift from app.Config. Signed-off-by: Andrei Ciobanu <aciobanu@godaddy.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Fixes #10
Summary
Adds a commerce (AP2) capability to the egress middleware as a concrete vehicle
for exercising and proving ANS identity enforcement end-to-end.
The egress fetches an authority-signed, scoped mandate, mints a DPoP proof, and
stamps a transparency-log-vouched ANS identity onto the outbound request, all
outside the (untrusted) sandbox so the workload cannot strip or forge it.
Commerce is implemented as a pluggable capability transform (discovered via
agent-card / MCP tools-list), not a hardcoded branch, so other capabilities can
reuse the same identity-stamping path.
A live two-agent demo (
deploy/live-demo/) shows enforcement both ways: avouched buyer agent transacts with the live seller and gets a ticket; an
unvouched dev-mode rogue making the same call is refused (
MANDATE_REJECTED).It runs against the live seller and the GoDaddy production transparency log, is
driven from a single
.env(no secrets or identity values in the repo), and hasa
--narratemode that displays the real decoded cert, mandate, and DPoP prooffor a reviewer to verify the chain themselves.
The companion seller-side DPoP clock-skew fix is tracked separately in
interop-agents-go.Testing
go build ./...clean.go test ./... -race -count=1green.golangci-lint run ./...reports 0 new issues.make coverat 95.6% (above the 95% gate).DEMO PASS, and--narrate --reportshows the real decoded identity/mandate/proof and writes amarkdown report. Confirmed the plain-run output is unchanged and that no
private key material is printed in any mode.
AI assistance
Assisted-by: Claude Code (claude-opus-4-8)
Checklist
git commit -s) certifying the DCO