Odexa 1.2.0-draft.2
Policy core
Scope, actions, purposes and obligations in the draft-1 policy vocabulary.
Policy version 1.2.0-draft.1, used by the draft-2 service profile. These sections preserve the tested draft-1 encoding, URL, evaluation and obligation rules. Section numbering is retained for cross-references. Service, receipt and evidence behavior is described separately in the implemented wire profile.
2. Encoding, discovery and policy contract
JSON MUST be UTF-8 without a byte-order mark, duplicate members, non-finite numbers or comments. Integer fields require lexical JSON integer tokens within 0–9,007,199,254,740,991 unless a narrower bound is stated: decimal digits only, no minus, decimal point or exponent. Negative zero (-0) is rejected, including for zero-allowed fields. Booleans are not integers; 1.0, 1e0 and precision-rounded fractions are rejected. The semantic parser must enforce this even when a generic JSON Schema validator considers an integral decimal an integer. Required strings MUST be nonempty. Unknown object members are rejected in this draft unless the object explicitly permits them. Core identifiers, actions, purposes and enumerations are case-sensitive. Timestamps are valid calendar timestamps in the exact form YYYY-MM-DDTHH:MM:SSZ; leap-second value 60 is not accepted in this draft.
A publisher MUST serve the current policy at https://<origin>/odexa.json, status 200 and Content-Type: application/json. This is an Odexa draft publication convention chosen by the origin owner, not an IANA-registered well-known endpoint. Discovery fetches require no credential. No redirect is accepted for this authoritative endpoint in draft 1. An HTTP Link using the extension relation https://odexa.io/relations/policy MAY additionally point to that same URL; that identifier is proposed by this draft, not a claim that documentation has been published there.
Core policy fields, all required:
| Field | Type and meaning |
|---|---|
protocol_version |
Exactly 1.2.0-draft.1 |
policy_id |
Absolute HTTPS identifier on the policy’s own origin for a stable policy lineage; no query or fragment |
revision |
Positive integer, increasing whenever any published policy bytes change |
origin |
Canonical HTTPS origin as defined in section 3 |
issued_at, expires_at |
UTC timestamps; issued_at < expires_at |
default |
Exactly no_grant |
resources |
1–256 resource objects with unique id values |
rules |
1–512 rule objects with unique id values |
A resource is {id, selectors}. id matches [A-Za-z0-9][A-Za-z0-9._:-]{0,127}. selectors contains 1–32 distinct objects {type, path, query}. type is exact or directory_prefix; query is absent or any. Paths follow section 3. A directory prefix MUST end in /. These IDs name policy scopes, not immutable delivered assets or asset versions. A selector covers the whole selected resource representation; partial-page CSS exclusions are outside this draft. Publish a deliberately licensed clean representation when a mixed-rights page cannot be licensed as a whole.
A rule is {id, resource_ids, actions, purposes, effect, obligations}. Its id follows the resource-token grammar. All three reference/vocabulary arrays are nonempty and contain no duplicates; each resource reference MUST exist. effect is permit, prohibit or require_agreement. obligations has 0–64 objects defined in section 5. Prohibition rules MUST have an empty obligations array.
The (policy_id, revision) pair MUST identify immutable bytes. The publisher MUST retain past revisions at immutable URLs for outstanding evidence/agreements; the agreement service provides their URLs when needed. A policy file is limited to 128 KiB (131,072 bytes) of decoded body, with a maximum JSON nesting depth of 32. Oversize, malformed or unsupported policy is invalid_policy. A well-formed policy outside its time window yields not_yet_valid or expired; none supplies permission.
Evaluator input is {url, actions, purposes} with optional supported_obligations (default empty array), plus a trusted now timestamp. Arrays are nonempty except supported_obligations. Repeated vocabulary items are invalid input. Supported obligation names are the exact type values from section 5. A client MUST list every action and intended purpose of its operation; it MUST NOT omit storage or training to obtain a retrieval-only result. The local evaluator has no HTTP-method argument and cannot itself enforce method or delivery. Draft-1 gateways allow only GET/HEAD and check the method separately before evaluation/introspection.
Example policy:
{
"protocol_version": "1.2.0-draft.1",
"policy_id": "https://publisher.example/policies/public",
"revision": 1,
"origin": "https://publisher.example",
"issued_at": "2026-09-16T00:00:00Z",
"expires_at": "2026-10-16T00:00:00Z",
"default": "no_grant",
"resources": [
{"id": "public-guides", "selectors": [
{"type": "directory_prefix", "path": "/guides/", "query": "absent"}
]}
],
"rules": [
{"id": "free-public-retrieval", "resource_ids": ["public-guides"],
"actions": ["retrieve"], "purposes": ["public_retrieval"],
"effect": "permit", "obligations": [
{"type": "attribution", "name": "Example Originator", "url": "https://publisher.example/"}
]},
{"id": "internal-use-agreement", "resource_ids": ["public-guides"],
"actions": ["retrieve", "store", "transform"], "purposes": ["internal_knowledge"],
"effect": "require_agreement", "obligations": [
{"type": "retention", "max_seconds": 2592000}
]},
{"id": "no-training", "resource_ids": ["public-guides"],
"actions": ["retrieve", "store", "transform", "redistribute"],
"purposes": ["model_training", "model_fine_tuning"],
"effect": "prohibit", "obligations": []}
]
}
This example grants retrieval for a source-linked public answer. It does not silently grant storage, transformation or redistribution; those require their own matching permissions. An application wanting all those actions must publish/request them explicitly.
3. Exact URI handling
These rules are intentionally conservative and MUST be shared by publishers, agents and gateways. A gateway MUST also verify that its HTTP router has not introduced an unexamined alias. Unsupported URL forms are rejected rather than guessed.
- Parse an absolute ASCII URI of at most 4,096 characters. Scheme matching is case-insensitive but only HTTPS is accepted. Reject user information, backslashes, whitespace, controls and raw
<,>,",{,},|,^, backtick anywhere in the URL. Internationalised hostnames must already use ASCII A-labels; do not run differing IDNA conversions inside the evaluator. - Hosts are lowercase DNS labels or canonical dotted-decimal IPv4. DNS labels contain ASCII letters/digits and interior hyphens, are 1–63 characters, and do not begin/end with a hyphen. Total host length is at most 253 characters. A trailing dot is rejected. IPv4 must be exactly four decimal octets, each 0–255, without leading zeroes except the single digit
0. IPv6 literals and ambiguous numeric-host forms are unsupported in draft 1. - Port is absent or a decimal integer 1–65535 without leading zeroes. Effective port 443 is omitted from the canonical origin. A published
originMUST already equalhttps://plus the canonical host and optional nondefault port, with no path, slash suffix, query or fragment. Request scheme/host case and an explicit:443normalise to this form before comparison. - Only request origins equal to the policy origin after this normalisation can match resources. A different valid origin yields
no_grant, not a syntax error. There is no subdomain, suffix or redirect inheritance. An empty request path normalises to/. - Raw paths contain only
/and ASCII RFC 3986pchar: unreserved characters, sub-delimiters,:and@, with well-formed percent escapes. Raw non-ASCII characters are rejected. Percent-encoded unreserved ASCII is decoded. Every other percent-encoded ASCII octet is rejected, including encoded slash, backslash, percent, question mark, fragment mark, spaces and controls. This prevents a second decoder from changing scope. - Percent-encoded octets at or above 128 are retained as uppercase
%HHand MUST form well-formed UTF-8 when the path’s escapes are decoded for validation. Reject invalid UTF-8, overlong sequences, surrogate code points and values beyond U+10FFFF. Do not apply Unicode normalisation. Encoded and unencoded reserved ASCII are not interchangeable in this profile. - Reject a path containing
//or a segment equal to.or..after unreserved decoding. Do not remove dot segments or merge slashes. Path matching is case-sensitive. - Strip a request fragment before determining HTTP resource scope. A literal
?before that fragment denotes a query even when its value is empty.query: absentmatches only when no query delimiter exists.query: anydeliberately covers all query values. Query content does not participate in path matching; it MUST still be ASCII, contain no whitespace/control/backslash and use valid percent escapes. No query decoding or sorting occurs. - Policy selector paths MUST already be in this canonical form, contain neither query nor fragment, and have a maximum length of 4,096 ASCII characters.
exactuses byte-for-byte path equality.directory_prefixuses byte-prefix matching:/guides/matches itself and descendants, but not/guidesor/guides-old/.
Examples: /a/%62 normalises to /a/b; /a/%2E%2E/b, /a//b, /a%2Fb and /a%252Fb are rejected. /guides/a? fails an absent selector. #section changes no fetched resource. A restricted route with unavoidable aliases must be adapted at the origin before gateway conformance is claimed.
4. Actions, purposes and deterministic decisions
Actions:
| Action | Meaning |
|---|---|
retrieve |
Obtain a representation, including an authorised GET or HEAD |
store |
Retain a copy, cache, persistent embedding or index beyond the immediate retrieval operation |
transform |
Derive a representation, summary, embedding or model update from the asset |
redistribute |
Provide the asset or a substantial copy/derived representation to another principal |
Purposes:
| Purpose | Meaning |
|---|---|
search_indexing |
Build a public search discovery index; does not independently authorise full-content answers or distribution |
public_retrieval |
Source-linked retrieval/answering for public discovery; does not include a separately maintained internal knowledge product |
internal_knowledge |
Maintain or operate a private/organisational knowledge index or retrieval product |
model_training |
Train model parameters, excluding the separately classified fine-tuning use |
model_fine_tuning |
Adapt an existing model’s parameters using the asset |
redistribution |
Supply content or derived asset collections as a product or onward distribution |
Purposes are not hierarchical. No vocabulary item implies another. “Commercial” versus “non-commercial” is not inferred from a purpose; price and additional agreed terms are separate. Unknown vocabulary makes input/policy invalid. An adapter to another standard must not silently broaden these meanings.
For every member of the Cartesian product actions × purposes:
- Validate the policy and request. An invalid policy yields
invalid_policy; otherwise an invalid request yieldsinvalid_request. Before rule evaluation,now < issued_atyieldsnot_yet_valid, andnow >= expires_atyieldsexpired. - Select resources with at least one matching selector, then ALL rules referring to a selected resource and containing the action and purpose. Rule order and selector specificity do not override another applicable rule.
- Any matching
prohibityieldsprohibit. - No matching rule yields
no_grant. - Combine obligations from all matching permit/agreement rules. If the client lacks support for any combined obligation type, yield
unsupported. - Otherwise any
require_agreementyieldsrequire_agreement; otherwise yieldpermit.
For the whole request, use this precedence: prohibit → no_grant → unsupported → require_agreement → permit. Return each tuple’s outcome and matched rule IDs as well as the overall outcome and combined obligations. In particular, one permitted action cannot hide an ungranted or prohibited action.
An input action/purpose list asserts the full Cartesian set. If a workflow intends (retrieve,public_retrieval) and separately (transform,model_training), evaluate those two operation requests separately rather than submitting two lists that accidentally assert four combinations. A workflow proceeds only where every actual operation has permission or the appropriate agreement. The returned access_credential_verified is always false for the local policy evaluator; agreement verification belongs to the service/gateway.
permit is conditional on performing the duties. It is not evidence that they were fulfilled. no_grant means Odexa supplies no affirmative permission for that tuple; it is not a statement that applicable law necessarily prohibits it. A broad prohibition cannot be overridden by a narrow permit. Publishers wanting exceptions must partition their declared scopes or deliberately revise the policy.
A service MUST NOT manufacture a grant for a prohibited/unlisted tuple. Its offers must remain within the policy’s permit/agreement outcomes, and any delegate’s narrower authority. A service can offer a recorded free agreement for a permitted tuple when requested; it cannot require that workflow retroactively as a condition of an otherwise unchanged static permit.
5. Obligations and free static use
The closed draft-1 obligation registry is:
| Object | Required behavior and combination |
|---|---|
{type:"attribution", name, url} |
name is a 1–256-character display string; url is an absolute HTTPS source URL without fragment. Credit both when presenting the resulting answer/copy. Combine distinct (name,url) pairs; deduplicate identical pairs. |
{type:"retention", max_seconds} |
Integer 0–9,007,199,254,740,991. Delete covered retained representations within this many elapsed seconds from acquisition. Copies/transformations do not reset the clock. Zero permits no persistence beyond the immediate operation. Combine by taking the minimum. |
{type:"report_usage", endpoint, deadline_seconds} |
HTTPS endpoint without query/fragment; integer deadline 1–9,007,199,254,740,991. Send a section-10 client report for each completed covered action or reporting interval within this many elapsed seconds of its completion. Combine each distinct endpoint; for repeated endpoints take the shortest deadline. |
No obligation removes another. Unsupported or contradictory duties do not become optional. Data subject to retention must not be licensed for irreversible training if the client cannot honour the specified deletion duty; it must decline or obtain revised terms that the originator is authorised to offer. Odexa does not claim that deleting a training input untrains a model.
For static operation, an agent fetches/revalidates policy, evaluates all proposed tuples, records the policy revision/digest and complies with resulting duties. It MUST NOT keep using cached policy after expiry or more than 300 seconds after successful validation, whichever is sooner. A new valid 304 response resets the validation time but not the policy expiry. A failed refresh supplies no new permission. Static permission requires no account, signature exchange, provider or payment. No acceptance receipt is created merely by reading a file.
Originators MAY observe HTTP delivery through their own server/CDN logs. Those observations must use section-10 labels if exported as Odexa evidence. A report_usage duty records a client’s assertion, not independent observation of that downstream use.
For reporting, retrieval completes when the client finishes the response or receives HEAD metadata; a store write completes when its retained representation is committed; a transformation or redistribution completes when that operation ends. Continuous storage and operations lasting more than 86,400 seconds additionally produce a reporting checkpoint at each 86,400-second interval from operation start. A checkpoint reports in_progress, not completed model training or completed retention. Final completion/failure is reported separately. The deadline runs from that completion/checkpoint time. Failed operations are not counted as completed use. A compliance percentage is valid only for known expected triggers within declared coverage; a missing client report does not reveal an unknown action’s completion time.