ObolNetwork / ObolNetwork/obol-stack
Align public discovery architecture with ERC-8004, A2A, MCP, OASF, and x402 standards
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 11
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Obol's public discovery surface should be modeled as one agent identity per public hostname, with ServiceOffer objects contributing service entries into that identity, rather than as many independent offer identities contending for a single /.well-known/agent-registration.json path.
This issue proposes a standards-aligned public architecture for the monetization stack so that Obol matches the intended separation between:
- ERC-8004: agent identity and trust
- A2A: agent-to-agent discovery and messaging via Agent Card
- MCP: tool/resource protocol endpoint
- OASF: machine-readable skill/domain metadata
- x402: payment gating on callable service endpoints
- ServiceOffer: Obol internal control-plane intent object
This is related to, but distinct from, the controller migration in #296.
Why This Matters
We have already discovered the practical symptom in the current sell-side architecture:
/.well-known/agent-registration.jsonis a singleton public path per hostname- but our current monetization model has historically treated registration as if it were per-ServiceOffer
- this creates an architectural mismatch, because multiple offers cannot each be the canonical owner of the same well-known identity document on the same hostname
The recent controller work has made this mismatch explicit by enforcing singleton ownership of the registration path. That is directionally correct, but it also exposes a deeper design question:
Is Obol Stack exposing one economic agent with multiple services, or multiple independent agents behind one hostname?
If the public hostname is singular, the standards-clean answer is:
One hostname => one agent identity => one agent registration file => many services
If we want multiple independent agent identities, then they need distinct hostnames/subdomains, not competing writes to one /.well-known/agent-registration.json.
Standards Baseline
1. ERC-8004: agentURI is the primary identity artifact
Per the ERC-8004 spec, the canonical on-chain identity pointer is agentURI, and it must resolve to the registration file.
- Spec reference:
ERC8004SPEC.md - Public spec: https://eips.ethereum.org/EIPS/eip-8004
- Canonical local reference:
~/Development/R&D/erc-8004-contracts/ERC8004SPEC.md
Relevant spec language:
agentURIMAY behttps://,ipfs://, ordata:- the registration file contains
services[] services[]can point to A2A, MCP, OASF, web, ENS, DID, email, etc./.well-known/agent-registration.jsonis an HTTPS hosting convention and also an optional endpoint-domain proof mechanism
Related references:
- https://github.com/erc-8004/erc-8004-contracts
ERC8004SPEC.mdagentURI and registration file section
2. A2A Agent Card is not the ERC-8004 registration file
A2A has its own discovery artifact, usually at:
/.well-known/agent-card.json
That artifact is linked from ERC-8004 services[]; it is not interchangeable with the registration file.
Practical references:
create-8004-agentscaffolds A2A card separately from registration- Lucid Agents hosts registration and A2A manifests as separate artifacts
References:
3. x402 belongs on service endpoints, not on identity endpoints
The registration file and agent card are discovery/identity artifacts. They should remain publicly readable and ungated.
x402 should gate the actual callable service endpoints:
/services/<name>/.../a2aif we intentionally monetize A2A calls- any HTTP endpoint that is the actual paid surface
Current Obol Mismatch
Historically, the system evolved from this mental model:
obol sell httpcreates oneServiceOffer- the reconciler creates one route
- the reconciler also publishes one registration document for that offer
- repeated for each offer
This works mechanically only while there is a single registered offer, but it becomes incoherent once multiple offers enable registration on the same tunnel/domain.
The mismatch in one sentence
We have been treating an agent identity artifact as if it were an offer-owned child resource.
That is the wrong boundary.
Clean Architectural Reading
The public model should be:
- Stack-level public agent identity
- Offer-level service exports
- Protocol-specific public artifacts linked from identity
In other words:
ServiceOfferis an internal declarative resource- the controller projects public standards artifacts from it
- public standards artifacts must remain standards-native and not leak internal ownership assumptions
flowchart LR
subgraph Internal["Obol Internal Control Plane"]
SO["ServiceOffer"]
CTRL["serviceoffer-controller"]
IDCFG["AgentIdentity config / CR"]
end
subgraph Public["Public Standards Surface"]
REG["/.well-known/agent-registration.json\nERC-8004 agentURI"]
CARD["/.well-known/agent-card.json\nA2A"]
MCP["MCP endpoint"]
OASF["OASF record"]
WEB["/services/<name>/...\nx402-gated endpoints"]
SKILL["/skill.md\nObol convenience catalog"]
end
subgraph Chain["On-chain"]
ERC8004["ERC-8004 Identity Registry"]
end
SO --> CTRL
IDCFG --> CTRL
CTRL --> REG
CTRL --> WEB
REG --> CARD
REG --> MCP
REG --> OASF
REG --> ERC8004
SKILL -. auxiliary only .-> WEB
Consequences of this reading
1. /.well-known/agent-registration.json is agent-level, not offer-level
It should describe the public agent identity attached to the hostname.
That means:
- one hostname
- one identity document
- one
agentURI - many
services[]entries
2. ServiceOffer should contribute entries, not own the identity document
A ServiceOffer should contribute:
- one
webservice entry - optionally one
A2Aentry if the offer exposes A2A - optionally one
MCPentry if the offer exposes MCP - optionally one
OASFentry if OASF metadata exists - pricing/description hints in the human-readable description or Obol-specific metadata
But the ServiceOffer should not be treated as the identity document's owner.
3. /skill.md is not the identity source of truth
/skill.md is useful as an Obol-native aggregate storefront/catalog, but it is not the standards artifact.
We should keep it only as:
- a human/machine-readable Obol convenience page
- derived from the same source of truth as the registration file
- never required for standards-based discovery
4. If we want one offer == one agent, we need separate hostnames
This is the clean branching point:
flowchart TD
H["One public hostname"] --> Q{"One or many agent identities?"}
Q -->|"One"| A["One ERC-8004 registration file\nAggregate services[] from many offers"]
Q -->|"Many"| B["Provision separate subdomains / hostnames"]
B --> C["One registration file per hostname"]
Trying to represent multiple public agents behind one hostname while preserving a single well-known registration path is not standards-clean.
Recommended Direction
Recommend that Obol explicitly standardize on:
One public hostname/tunnel per stack => one public agent identity per stack
Then build the architecture around that.
Stack-level identity
Introduce an explicit stack-level source for public agent identity, e.g.:
AgentIdentityCR, or- stack config (
obol-stack-config/ frontend config / dedicated ConfigMap)
This object should own:
- name
- description
- image/icon
- supported trust modes
- identity-level wallet/account metadata
- optional protocol-level metadata (default A2A / MCP / OASF pointers)
Offer-level exports
Each Ready ServiceOffer contributes one or more services[] entries to the aggregated registration file.
Minimum default:
web-> paid HTTP route for that offer
Optional:
A2A-> if the offer actually serves an agent card + A2A endpointMCP-> if the offer actually exposes MCPOASF-> if the offer publishes an OASF record
Controller responsibilities
The controller should derive and publish:
- Aggregated ERC-8004 registration file
- Optional Obol storefront
/skill.md - Per-offer x402-gated service routes
- Protocol-specific artifacts when enabled
What should not happen
- no per-offer ownership of the singleton well-known registration path
- no implicit claim that every paid HTTP route is a standalone public agent identity
- no coupling of the registration file to one arbitrarily winning offer's metadata
Proposed Data Model Split
erDiagram
AgentIdentity ||--o{ ServiceOffer : exports
AgentIdentity ||--|| RegistrationDocument : publishes
AgentIdentity ||--o{ ProtocolEndpoint : advertises
ServiceOffer ||--o{ ProtocolEndpoint : contributes
ServiceOffer ||--|| PaymentRoute : gates
AgentIdentity {
string name
string description
string image
string[] supportedTrust
string publicBaseURL
}
ServiceOffer {
string namespace
string name
string type
string effectivePath
string price
bool ready
}
RegistrationDocument {
string agentURI
string[] registrations
string[] services
}
ProtocolEndpoint {
string protocol
string endpoint
string version
}
PaymentRoute {
string path
string price
string network
string payTo
}
Relationship to #296
#296 is about moving reconciliation into a controller and separating control-plane concerns from the data plane.
This issue is the standards-model complement to that work:
- #296 asks: what runtime architecture should reconcile the system?
- this issue asks: what public standards architecture should that reconciler project?
They should converge on the same answer:
- internal control plane may be offer-oriented
- external identity and discovery must be agent-oriented
Concrete Changes Proposed
Phase 1: Make the public ownership model explicit
- Add architecture docs stating: one hostname => one agent identity
- Document that
/.well-known/agent-registration.jsonis stack-level - Document that A2A card is separate and linked from
services[] - Document
/skill.mdas auxiliary only
Phase 2: Introduce stack-level identity source
- Add
AgentIdentityCR or config source - Move identity-level metadata off individual offers
- Keep per-offer protocol/service metadata only where it belongs
Phase 3: Aggregate services[]
- Build one registration document from:
- stack identity metadata
- all Ready exported services
- Ensure deterministic ordering and stable updates
- Include on-chain
registrations[]from the single public agent identity
Phase 4: Clarify protocol projection
- only emit
A2Aif we actually serve/.well-known/agent-card.jsonand/a2a - only emit
MCPif an MCP endpoint exists - only emit
OASFif an OASF artifact exists - keep
webas the default projection for x402-gated HTTP services
Phase 5: Remove residual compatibility confusion
- remove or clearly demote legacy verifier-owned
/.well-knownserving path - ensure the controller is the only owner of public identity artifacts
- keep compatibility endpoints only if explicitly documented as fallback behavior
Acceptance Criteria
- Obol docs clearly distinguish:
- ERC-8004 registration file
- A2A Agent Card
- MCP endpoint
- x402-gated service route
/skill.md
- A single public hostname publishes exactly one ERC-8004 registration document.
- The registration document is derived from stack-level identity plus aggregated Ready services.
- Multiple registration-enabled offers do not contend for the same well-known identity path.
- A2A/MCP/OASF entries only appear when the corresponding artifacts actually exist.
/skill.mdis published as a convenience catalog, not as the canonical identity or discovery surface.- If future product direction requires one-offer-one-agent, the design explicitly provisions separate hostnames/subdomains.
References
Internal repo references
- #296 — controller migration and control-plane cleanup
docs/monetisation-architecture-proposal.mddocs/guides/monetize-inference.mdCLAUDE.mdmonetization/discovery notes
External standards and ecosystem references
- ERC-8004 spec: https://eips.ethereum.org/EIPS/eip-8004
- Canonical contracts/spec repo: https://github.com/erc-8004/erc-8004-contracts
create-8004-agent: https://github.com/erc-8004/create-8004-agent- Lucid Agents: https://github.com/lucid-agents/lucid-agents
- x402: https://www.x402.org/
- A2A: https://a2a-protocol.org/
- MCP: https://modelcontextprotocol.io/
Locally validated references used to formulate this issue
~/Development/R&D/erc-8004-contracts/ERC8004SPEC.md~/Development/R&D/create-8004-agent/README.md~/Development/R&D/lucid-agents/lucid-docs/content/docs/packages/identity.mdx~/Development/R&D/lucid-agents/lucid-docs/content/docs/concepts/agents.mdx
Suggested Outcome
Adopt this as the canonical public architecture principle for Obol monetization:
Obol Stack is one public economic agent with multiple exported services, unless we explicitly provision separate public hostnames for separate agents.
That aligns the product with ERC-8004, A2A, MCP, OASF, and x402 instead of treating those standards as interchangeable or collapsing them into ServiceOffer ownership semantics.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with ERC8004SPEC.md, issue #296, and the current controller and verifier ownership of /.well-known/agent-registration.json. Define the stack-level identity source and aggregation boundaries before implementation. Done means one registration document per hostname, aggregated Ready services, distinct A2A/MCP/OASF artifacts, and x402 only on callable service routes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, helm, kubernetes
- Domain
- backend, cloud, devops
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100