1.2.0-rc.1 / Agreements and authority

Authority discovery

Acquire current origin policy and authority over verified HTTPS.

17 September 2026. Unpublished draft-3 candidate work. This adapter supplies actual HTTPS observations to the delegated-authority engine. It is not a complete delegated agreement service or an RC.

API and trust boundary

from profiles.authority_transport import AuthorityTransport, ScopedCredential

transport = AuthorityTransport(
    ca_file=None,            # optional explicit trust store, including a fixture CA
    allowed_ips=(),         # exact exceptions to the public-address policy
    deadline_seconds=3.0,
)
snapshot = transport.fetch("https://publisher.example")
# snapshot.authority_bytes, snapshot.policy_bytes, snapshot.observation

The successful frozen dataclass preserves both exact response bodies. Its observation names the origin and exact publication URLs, includes the two SHA-256 digests, sets source=https, and records verified TLS, no redirect and current revalidation. checked_at is sampled before DNS and either fetch; slow retrieval cannot mint a later timestamp that makes old input appear fresh. Observation fields are a trusted adapter result, never copied from an agent, provider quote or HTTP response body. The returned observation dictionary must remain within the trusted process; the frozen wrapper does not make a nested Python dictionary tamper-proof.

Use the two byte strings and observation together in evaluate_authority. The policy/delegation engine still validates schema/semantics, publication identity, current validity and operation scope. A successful fetch alone grants nothing and does not authenticate any JWS. History/rollback checks and operation decisions must consume the snapshot before committing a mutation. Current-authority history is owned by a separate adapter; this module neither persists history nor has a stale snapshot to reuse.

The wall clock is trusted operating-system UTC; monotonic time independently enforces deadlines. utc_now() exposes the UTC formatter for consumers. A host with a materially wrong clock is outside this adapter’s trust assumptions. The engine must check freshness at the actual decision, and the transactional consumer must sample its clock after waiting for the database lock.

Network and credential rules

The origin must be a canonical HTTPS origin under the frozen native URL rules. Only /odexa-service.json and /odexa.json are fetched. Each acquisition resolves the origin once, rejects an empty/excessive answer and vets every returned IP before connecting. The selected numeric address is pinned for both connections; TLS SNI and hostname/IP certificate verification still use the original origin hostname. There is no hidden DNS re-resolution or retry against a changed answer. A failing first address fails the operation; a later caller may start a new acquisition.

By default every resolved address must be public/global, excluding multicast, reserved, unspecified and loopback addresses. Private/loopback destinations require an explicit exact IP in allowed_ips; there is no wildcard, CIDR or general private-network switch. Fixture tests allow only 127.0.0.1. A mixed answer containing a disallowed IP fails entirely. IPv4-mapped and other address classification follows the runtime’s ipaddress library; DNS origin spelling follows the protocol’s existing parser.

The resolver’s caller wait is bounded. Because libc DNS lookup cannot be cancelled portably, an over-time lookup may finish in a daemon thread, but that worker cannot create a connection. Deployments must also bound concurrent acquisitions; this library is not an unlimited request scheduler.

TLS certificate-chain verification and hostname matching are mandatory, with TLS 1.2 or newer. The optional CA file supplies explicit trust for synthetic/local deployments and does not install a system certificate. Disabling verification on the context is rejected. The transport uses direct sockets, ignores proxy environment variables and has no cookie jar, .netrc, browser credentials, ambient Authorization header or redirect handler.

This is a safe default destination filter, not a universal SSRF policy. A deployment must authorize which publishers a tenant may contact and apply its own egress rules. Public addresses and ports can still expose services that an application should not contact. An explicitly allowed private address authorizes that destination for this transport instance, so fixture exceptions must not be copied indiscriminately into public service configuration.

Current fetch and bounded content

Both GETs send Cache-Control: no-cache, no-store, Pragma: no-cache, Accept: application/json, Accept-Encoding: identity and Connection: close. The authority response must declare Cache-Control: no-store. Policy is explicitly revalidated on every acquisition; conditional or stale responses are not used. Nonzero Age and cache Warning response headers are rejected. These rules rely on compliant origin/cache behavior; a dishonest origin can still serve old bytes. Durable monotonic revision/digest history supplies a separate rollback defense.

Only the expected successful HTTP status is accepted, with no redirects. JSON media type is application/json, optionally UTF-8 charset; other charsets, duplicate Content-Type, compressed bodies, transfer encoding and ambiguous length framing are rejected. Exactly one canonical Content-Length is required. Each authority/policy response is at most 128 KiB. The strict JSON parser rejects duplicates, fractional/exponent/negative integer tokens, non-finite numbers, invalid UTF-8, surrogate strings and excessive nesting. The decoded value must be an object. Semantic authority/policy validation remains the engine’s responsibility.

The adapter uses one total acquisition budget, including DNS, connection, TLS, both headers and both bodies. Default is three seconds, configurable from 0.05 through five seconds. A watchdog shuts down the active socket at the absolute deadline, so a server cannot extend the budget by trickling a byte before each socket timeout. The HTTP parser imposes its own header-count/line bounds; the adapter additionally rejects a completed header section exceeding 32 KiB. Returned bodies preserve the exact HTTP-framed bytes; unused bytes after Content-Length are not interpreted and the connection is closed.

Any first/second fetch, transport, framing, parsing or cache-policy failure raises TransportError and returns no snapshot. There is no partial result or stale fallback. A successful prior fetch does not affect a later failed acquisition. Two sequential responses are not a globally atomic publisher snapshot; the engine cross-checks policy identity, and durable history and the eventual service commit enforce their additional invariants.

Explicit provider request primitive

The same transport exposes a narrow direct request method:

response = transport.request(
    "https://provider.example", "/tenant-a/api/verify",
    method="POST", body=exact_check_json_bytes,
    credential=ScopedCredential(
        "https://provider.example", "/tenant-a/api/", "Basic ..."
    ),
    response_type="application/jose", max_bytes=262144,
)
# response.status, response.headers (tuple), response.body, response.peer_ip

Only GET/POST and canonical paths without query or fragment are supported. Outbound POST bodies are either strict JSON objects (application/json, at most 128 KiB) or bounded compact JWS (application/jose, at most 256 KiB wire bytes and 128 KiB decoded payload); GET has no body. The larger JOSE wire ceiling accommodates base64url/signature overhead without reducing the protocol’s payload allowance. JOSE parsing validates the protected-header/payload/signature encoding. Its default allowed_jws_types=crypto.TYPES retains the existing registry; a trusted internal adapter may pass an explicit nonempty set/frozenset, such as frozenset({'odexa-payment-mandate+jws'}). This per-call selection never changes crypto defaults, authenticates an outgoing signature or comes from an untrusted request. The transport retains exact supplied bytes. No caller-defined headers are accepted. Credentials must match the exact origin and canonical directory prefix ending /, protecting boundaries such as /tenant-a/api/ versus /tenant-a/api2/. Authorization values are bounded printable ASCII without controls and excluded from the credential object’s repr. The public authority fetch method never accepts or forwards such credentials.

Responses are JSON or application/jose as explicitly selected; JOSE bodies must be ASCII, at most 256 KiB. JOSE syntax, typ, signature, issuer/key and request binding are the consuming verifier’s responsibility. expected_status defaults to 200 and may be an explicit tuple of 1–8 distinct successful status integers, such as (200, 201) for original/retried intake. Unlisted successes and all redirects remain rejected. The primitive uses the same DNS, address, TLS, cache-age, framing and absolute-deadline rules, but a generic provider response need not declare no-store. It returns exact bytes and never charges, follows a callback, executes a payment, retries or interprets provider status.

The optional trusted before_send callable runs after DNS/connection/TLS handshake and immediately before HTTP headers, credentials and body are transmitted. It must return the exact boolean True; any other result or exception aborts the request. The adapter checks its absolute deadline again after the callback. This hook is internal code, never an agent-supplied callback. The payment adapter uses it to require the still-current history snapshot and then evaluate authority using a freshly sampled clock, closing the gap where DNS, TLS or a history lock could make the earlier decision stale before disclosing payment context to the provider.

A payment adapter must resolve current authority for its operation, authenticate the provider response, re-evaluate appropriate current authority before commit, and compare exact quote/mandate/check/request bindings. A successful network call cannot replace those checks. Remote authority fetch and local commit remain non-atomic: retained authority revision, digests and times describe the observed boundary, not instantaneous global revocation.

Verification scope

test_authority_transport.py contains 19 focused test methods using two actual HTTPS origins on separate ephemeral loopback ports and a generated private CA. It covers exact bytes passed into evaluate_authority, default private-address rejection, untrusted CA/wrong hostname, one DNS resolution/pinned connections, mixed DNS rejection, redirect isolation, no stale fallback, no-store/cache-age policy, strict framing/media/JSON, truncated/trickled bodies, cumulative deadlines, bounded resolver wait, explicit provider credential scope, denial after TLS before any HTTP transmission, exact signed JOSE request bytes with unsupported-media rejection, explicit success-status sets and per-call JOSE types without registry mutation.

python3 -m unittest discover -s tests -p 'test_authority_transport.py' -v

The integrating task previously ran all 17 tests in the frozen r3 suite successfully; the two new success-status/type-selection tests await its current run. The child worker’s local server bind is sandbox-blocked. No public network request is needed. The first authority test fetches only the publisher and verifies the external provider remains untouched; a separate generic-request test actually contacts the provider. This suite alone is not the complete origin–provider–origin payment exchange or full protocol interoperability; the root’s separate network-payment suite records that bounded exchange’s evidence.

Odexa / Protocol explorer

This page. Your terms.

Inspect this website’s published policy and see how a proposed use is evaluated.

Current pagehttps://odexa.io/guides/authority-transport/
Loading policy…

Published JSON
Open JSON

This is a local policy check, not a signed agreement or proof of agent compliance. Other published licences and applicable rights still apply. How policy evaluation works →

Odexa / Get in touch

Start a conversation.

Tell us what you have in mind. We’ll respond where we can.

We use these details to review and respond to your enquiry. Please leave out confidential information. Submitting does not subscribe you to marketing. Privacy policy.