Reference implementation

Portfolios resource

A synthetic portfolio set behind two detail types: read a collection, or write a note on one record. Nothing here moves value.

Reference implementation, built to demonstrate integration. Operated by Tarnbrook Platforms in the tarnbrook-agents domain. It exists to show what a resource can demand of an agent, and to show that a refusal can always be explained.

What an agent has to discover

Resourcehttps://portfolios.agents.showcase.raidiam.io
Authorization serverhttps://api.agents.showcase.raidiam.io
Trust anchorhttps://federation.directory.showcase.raidiam.io/federation_entity/7b60c9fa-00dd-46d9-80de-cd899ee5a58d
Detail typesportfolios.read, portfolios.annotate
Sender constrainingDPoP verified when presented, required for bound tokens
Mutabilityaccepts instructions
Signing key custodyA public key registered on this resource's record, nothing issued. The authority vouches the supplied key by its kid; the private half never leaves this service.
Limitsno per transaction limit

Authorization detail types

portfolios.read

Read discretionary portfolio records from named collections.

Required members: type collections

Optional members: none

{
  "type": "portfolios.read",
  "collections": [
    "client-records"
  ]
}

portfolios.annotate

Write a note on a portfolio record. Moves nothing.

Required members: type collections

Optional members: none

{
  "type": "portfolios.annotate",
  "collections": [
    "client-records"
  ]
}

Token claims this resource decides on

Tools

ToolPurposeRequiresEffect
read_portfolios Read discretionary portfolio records from a collection. portfolios.read read only
annotate_portfolio Write a note on a portfolio record. Moves nothing. portfolios.annotate changes state

Guardrails, published in advance

Every call carries a token from the named authorization server, resolved through the federation

Calls are accepted only with an access token issued by https://api.agents.showcase.raidiam.io, addressed to this resource as its audience, and verified against keys that the walked chain from that server to the pinned trust anchor vouches. A token minted for a different resource, or signed with a key the chain does not publish, is refused even when it is otherwise valid.

Refusal reason missing_access_token, invalid_token, issuer_unresolved, decided at authorization, policy id rs.authenticated_caller

What clears it: Read this metadata document, then request a token from the authorization server it names, with this resource as the audience.

Authority is the RFC 9396 detail type, not a scope

Each tool names one authorization_details type. The token must carry that type, or a type that narrows to it in the published type tree. Holding a scope, or holding authority for a neighbouring resource, does not admit the call.

Refusal reason insufficient_authority, decided at authorization, policy id rs.authority_gate

What clears it: Obtain a token carrying the detail type the tool names.

Sender constrained tokens are bound to the key that holds them

A DPoP proof is verified whenever one is presented, and is required whenever the access token names a key in its cnf.jkt claim. Each proof is accepted once, so a captured proof cannot be replayed.

Refusal reason dpop_proof_required, invalid_dpop_proof, dpop_key_mismatch, dpop_proof_replayed, access_token_not_dpop_bound, decided at authorization, policy id rs.dpop_binding

What clears it: Request the access token with a DPoP proof so the authorization server binds it to your key, then send a fresh proof with every call.

A malformed request is refused with the reason it was malformed

Arguments are validated before any business rule runs, and the refusal names the argument at fault rather than returning a bare failure.

Refusal reason tool_error, invalid_amount, unknown_tool, decided at execution, policy id rs.request_validity

What clears it: Correct the named argument. The tool schemas are published in this document.

A grant names the collections it reaches

Both detail types carry a collections member. A call that names a collection outside the grant is refused before any record is read, and the refusal names the collection at fault.

Refusal reason collection_not_granted, decided at authorization, policy id portfolios.collection_granted

What clears it: Ask for a grant whose collections member names the collection.

A note changes a record and never moves value

portfolios.annotate writes a note against one record. There is no path from this resource to a transfer, so the widest authority it can exercise is a sentence on a file. portfolios.read reads and changes nothing at all.

Refusal reason record_not_found, decided at execution, policy id portfolios.annotate_moves_nothing

What clears it: Name a record that exists in a collection the grant reaches.

Observability

Every admission decision, allowed and refused, is recorded with the checks that ran, the policy that decided it and the values it turned on. Read them at /decisions, or one run at a time by trace id.