Changelog
What changed in each version of the specification, as listed at the end of the specification itself.
Subscribe to the Atom feed to get new versions in your feed reader.
Atom feedv0.1.1
In the specification Corrections and a security fix. The type of every format stays the same. Three things can
change an outcome, each named below: a noncharacter is no longer accepted in a new document,
a leap second in an audit entry stops verifying in the reference verifier, and a signature
under an Ed25519 key of small order stops verifying.
- Correction
noncharacters(Section 14, a new kind of change, anddata/corrections-v0.json): RFC 7493 forbids noncharacters in member names and string values, and Section 3.1 item 1 refers to it, but the list in that item did not name them and the reference implementation accepted them. Item 1 now names them, and Section 9.1 names input that is not I-JSON among what the log cannot record. A new entry MUST NOT contain one. A verifier keeps accepting them in existing audit logs, so that no unaltered log becomes invalid; the outcome of the verification does not change. A stored document with a noncharacter is attributed as before (Section 4.3). No valid mandate changes: the free-form strings of a mandate are displayed text, which excludes noncharacters already, or printable ASCII. New conformance casesa213toa220,i158toi163,g24,s29,d52; beyond the correction, they cover duplicate names after escapes, every form of a lone surrogate, the legal pair and the number examples of RFC 7493. - Audit entries: a leap second in
recorded_at,request.timeorapproval.atis invalid, as Section 3.1 item 0 says and the schema's description already referred to. The pattern in both schemas now excludes it. The reference verifier accepted it; spec-ts did not. This is not a correction (Section 14), because implementations did not read it alike, so a log with such an entry stops verifying in the reference verifier. No writer I know of produces one. - A
seqthat is no integer, however close, givesbroken_at0 (Section 9.4); the reference verifier read it through floating point. A zero is zero whatever its exponent (Section 3.2), also beyond the range of a machine integer: such documents become valid in the reference implementation, as Section 14 allows. - Test interface: a
timethat does not fit the grammar of RFC 3339 is not readable (Section 10.2). The reference harness and its checkpoint signing accepted offsets such as+24:00. - New conformance cases
a221toa228,c243toc251,d53,i164toi175, which also cover bidirectional controls and separators in displayed text, field by field. - Security (GHSA-grm8-w7m9-fxj9), Section 7.1: an Ed25519 public key of small order is
rejected, because with it a signature verifies for every message of a class; the member
names of a JWK and a JWK Set are case-sensitive,
xandyallow no other character, and a memberdwith any value is a private key. One such key makes the whole key set unusable. Section 10.2:verify_signedwith such a key set answers{"valid": false}. This can change an outcome: a signed mandate or a checkpoint verified with a key of small order stops verifying. That is the purpose, since anyone could have produced those signatures. A key that combines a normal point with one of small order stays accepted: only its holder can sign with it, so nothing can be forged. New conformance casesg25tog35; checkpoints are covered by a test of the reference code.
v0.1.0
In the specification First release without a pre-release suffix. The v0 formats (mandate, audit entry, audit
checkpoint) and the vocabulary v0 are frozen from this release on (Section 14). The
entries below are every change since the rename, through the pre-releases v0.1.0-alpha.1
to v0.1.0-rc.5 and the changes after v0.1.0-rc.5; v0.1.0 itself changes no rule, format
or conformance case (conformance manifest of commit 116d009).
Renamed: the specification formerly published as mandate-spec is now the Home-Mandate
Specification; repository github.com/home-mandate/spec, Go module
github.com/home-mandate/spec, root package spec. Versions start again at
v0.1.0-alpha.1. The releases of mandate-spec (up to v0.2.0-alpha.5) remain available
from the Go module proxy under the module path github.com/mandate-spec/mandate-spec; their
changes are listed below.
Audit mode (Sections 2, 3, 3.1, 3.2, 3.5, 4, 4.1, 4.4, 4.6, 6, 8, 9.1, 9.2, 9.4, 10, 11, 12, 13):
- New optional member
enforceof the mandate, only everfalse(true, a string, a number ornullare invalid, and a rule has noenforce). A mandate withenforce: falseis in audit mode: adenythat results from its rules (no_matchor adenyrule) for an action that is not critical is evaluated and recorded but not enforced; the PEP executes the request as if it were allowed. Critical actions,ask,allow, every denial before the rules (invalid mandate or request, unknown resource, category or action, revoked, not yet valid, expired) and every denial outside the evaluation (rate limit, emergency stop, authentication, approval) are enforced as before. The mandatetypestayshttps://home-mandate.org/mandate/v0. Compatible: every mandate that was valid stays valid, is evaluated the same way and keeps its digest. - The result of the evaluation carries
enforced: falsefor such adeny; the decision staysdeny. In AuthZEN it is a member of the response context (Section 6); a PEP that does not know it enforces the denial. In the test interface it is a member of theevaluateandselectresponse and compared over HTTP (Section 10); the conformance cases have the new fieldenforced. - Audit entry:
evaluation.enforced(onlyfalse, only for adecisionwithevaluation.decision: denyand reasonno_matchorrule); only with it may adenybeexecutedorfailed, and it is neverdeniedunless it also carriesconfirm(see "Audit mode andask" below). The audittypestayshttps://home-mandate.org/audit/v0. Compatible for logs: every entry that was valid stays valid. A verifier that does not know the member rejects a log that contains it (Section 14, unknown members). - With
evaluation.enforced: false, an entry namesrequestwith thecategoryof the resource; it is invalid for a resource markedcritical(except forread), and a verifier MUST reject it for an action that is critical in the vocabulary or not in it (Sections 9.1, 9.4);request.resource.criticalis then required,trueorfalse, so that the marking cannot be left out. The reference verifier (audit) checks this against vocabulary v0 and against extension vocabularies it is given (audit.Options.Vocabularies,audit.VerifyWith,audit.VerifyJSONLinesWith); without the vocabulary of an extension, a verifier cannot check its critical actions. - Obligations of the PEP (Section 11.6): the agent is told nothing about audit mode, nor
which resources of a request for several were not enforced (Section 4.4);
implementations MUST show audit mode wherever they show mandates and SHOULD warn when a
resource marked critical can be read under it; switching it is a
mandate.updated. An implementation that does not offer audit mode MUST refuse to store a mandate withenforce: false. - Switching audit mode on (a mandate with
enforce: falsewhile no mandate with itsidin audit mode is valid, active and in force, which includes a predecessor that is revoked, expired or not yet valid, a mandate from outside and a restore from a backup) MUST be confirmed explicitly by a human of the household before it takes effect, also for a signed successor. The confirmation is bound to the digest of the version that is adopted. Every approver MUST be notified when the switch is proposed and when it takes effect (Sections 3.5, 4.6, 11.6). Switching it off needs no confirmation. - A confirmed
askis not executed if the evaluation before the execution yields anydeny, enforced or not; it is recorded with the confirmedask,approvedanddenied_by: mandate(Sections 9.1, 11.1 item 6). - Conformance cases
c174–c192(new mandateconformance/mandates/audit-mode.json),i144–i148,d43–d44,a111–a125. - Reference evaluator (Go):
Result.NotEnforced,Mandate.EnforceandEntersAuditMode(whether adopting a mandate switches audit mode on, compared with the stored mandate only if it is valid, active and in force).
Permitted values and string parameters (Sections 3, 3.1, 3.2, 4, 4.5, 5, 6, 8, 9.1, 10, 12):
- A parameter is an integer or a string. The vocabulary declares the type of every
parameter (
type:integerwith aunit, orstring, optionally with permittedvalues); strings follow the rules of an area: 1 to 64 printable ASCII characters without space, compared exactly and case-sensitive. There are still no fractions. Incompatible for extension vocabularies (beforev0.1.0): they must declare thetypeof every parameter and list at most 32 permitted values; mandates stay valid. - New constraint
one_of: a list of 1 to 32 distinct permitted values of the parameter's type, for integer and string parameters; not together withminormax, which exist only for integer parameters. A rule withone_ofmatches only if the request carries the parameter with one of the values. A constraint that does not fit the declared type, or a string value outside the permitted values of the vocabulary, makes the mandate invalid (Section 3.1 item 10). Every mandate that was valid stays valid: a constraint withminormaxon a parameter of the vocabulary was on an integer before as well. climate.set_modegets the string parametermodewith the permitted values of the Matter Thermostat SystemMode:off,auto,cool,heat,emergency_heat,precooling,fan_only,dry,sleep. A household can now allow heating modes but neveroff. The vocabulary keepsv0: it changes before the formats are frozen withv0.1.0.other.setstays critical and without parameters.- A request is invalid (
invalid_request) if a parameter is neither an integer nor such a string, if a parameter the action declares has another type ("50"forbrightness,4formode), if a string is not among the permitted values, or if an integer lies outside the range of the vocabulary (brightness,positionandvolume0 to 100;temperatureandtemperature_fhave none). Before, the range was informative, andbrightness: 101was evaluated like any other value. Constraints beyond the range stay valid but never match. Before,set_modehad no parameters, and a PEP could pass any value without effect. - In the test interface (Section 10), in AuthZEN (
action.properties) and in the audit entry (request.parameters), a parameter value is a JSON integer or, new, a JSON string. Incompatible: a string is never read as a number; the harness of the reference code formerly accepted"50"as the integer 50. The audittypestayshttps://home-mandate.org/audit/v0: the change comes before the formats are frozen. The audit loga37carries a boolean instead of the string"40"(a string is now a valid value) and is chained anew; its expected result is unchanged. - Numbers are read by their exact value: a fraction is never rounded to an integer, however
small it is (
50.00000000000000000001,1e-400). The reference code formerly read request parameters through floating point and took such values for integers. - Conformance cases
c152–c173(new mandateconformance/mandates/one-of.json),i129–i143,d41–d42,a109–a110. - Profiles: Home Assistant and Matter map their climate modes to the values of
mode; Home Assistantheat_coolandautoboth map toauto(Section 12.4). - Reference evaluator (Go), before
v0.1.0:Request.Parametersis amap[string]Value;Valueis an integer or a string (IntegerValue,StringValue) and reads and writes itself as JSON;Limit.OneOfholds the permitted values of a constraint.
Sets of areas (Sections 1.1, 2, 3.4, 4, 6, 8, 9.1, 10, 12):
- A resource has a set of 0 to 16 areas instead of at most one: its room and every
location above it (for example garage, ground floor, home), or several rooms side by
side. A rule still names exactly one
areaand matches if that area is in the set; the mandate format does not change, and every mandate that was valid stays valid. A request with more than 16 areas, the same area twice or an area that is no identifier is invalid (invalid_request). The duty to report affected mandates covers every area in the set. - Incompatible: in the request of the test interface, in the directory of the HTTP binding,
in AuthZEN (
resource.properties.areas) and in the audit entry (request.resource.areas),area(a string) becameareas(an array). An audit entry withrequest.resource.areais invalid. The audittypestayshttps://home-mandate.org/audit/v0: the change comes before the formats are frozen withv0.1.0. The audit logs inconformance/audit-v0.jsoncarryareasand are chained anew; every expected result is unchanged. - Conformance cases
c140–c151(new mandateconformance/mandates/areas.json) anda106–a108. - Test interface (Section 10): a request with a member the section does not define is
malformed, so that a former
areais not evaluated as if it were absent.
Temperatures in degrees Fahrenheit (Sections 4.5, 5):
- New parameter
temperature_fofclimate.set_temperaturein hundredths of a degree Fahrenheit, next totemperaturein hundredths of a degree Celsius, so that households whose platform uses °F can constrain temperatures exactly. The PEP derives exactly one of the two, in the platform's unit, and never converts. Compatible for implementations that load the vocabulary: a constraint ontemperature_fwas invalid before. The vocabulary keepsv0: the parameter is added before the formats are frozen withv0.1.0; from then on Section 5.1 holds without exception. Conformance casesc133–c139,i128. - Section 4.5 says plainly that a value that cannot be expressed as an integer in the unit makes the PEP refuse the request; it never leaves the parameter out.
- Section 11.5 (new): a PEP MAY tell an agent the limits of its own mandate (the
constraints of the
allowrules that match apart from them, in the agent's units), but never for a resource the agent may not read, of another mandate, ofdenyoraskrules, in a unit it never derives, or while rules on a former identifier still apply. The reference evaluator (Go) offersMandate.Limitsfor it; it decides nothing.
Overview (Sections 1.1 and 2, informative, no change to any rule):
- Section 1.1 (new) shows in a diagram how agent, implementation (PEP, PDP, audit log), smart-home platform and human work together, which of these interfaces the specification defines and which it leaves open. It names MCP as the agent interface of the reference implementation, as an example.
- Section 2 defines PDP and PEP.
The only path to the platform (Sections 12.2, 12.3, 12.4, text only, no change to any rule or format):
- Section 12.3: new assumption that the agent reaches the platform only through the PEP, holds no other credential for it and cannot create or change scripts, automations, rules or other code that executes on it. Section 12.2: new row for an agent with a path past the PEP.
- Sections 12.2 and 12.4: automations and rules that react to a harmless-looking resource; such a resource is marked critical, and interfaces show the household which resources automations react to where the platform can tell (openHAB as the example).
- Section 12.4: no tools that write platform configuration through the PEP (a request for
configuration is
deny); the PEP's own platform credential is as restricted as the platform allows and never given to the agent.
MCP elicitation (Section 11.1, informative, no change to any rule):
- Note after the list of Section 11.1: URL mode elicitation (MCP
2025-11-25and later) can be a confirmation path in the sense of item 4 when the PEP checks that the person who confirms is an approver and binds the page to exactly one request; form mode elicitation cannot, because the answer passes through the agent's client. In both cases a PEP still needs a path to the approvers.
Confirmations for several resources (Sections 4.4, 11.1):
- A confirmation for a request that the PEP resolves into several resources covers exactly the resolved set; the human is shown every resource of it (Section 11.1 item 2).
- Before the execution the PEP resolves the request again and executes only if the set is
unchanged; a resource added, removed or replaced in the meantime prevents the execution
for the whole set, also when the mandate is unchanged and every resource still evaluates
to
askorallow(Section 11.1 item 6). New RECOMMENDED coderesources_changed(failed). No change to any format. - The PEP executes a request for several resources on exactly the resources it evaluated and names each of them to the platform, never the area, group or list it resolved them from, so that the platform cannot add a resource that was not evaluated (Section 4.4).
Versions (Section 14, rewritten):
- The v0 formats (mandate, audit entry, audit checkpoint) are frozen from
v0.1.0, the first release without a pre-release suffix. After that, every incompatible change requires a newtypewith the next major version; a document that was valid stays valid and is evaluated the same way. Untilv0.1.0, incompatible changes remain possible and are listed here. - Defined what is incompatible: a valid document becomes invalid, or a valid document is evaluated differently (result, succession, digest, verification of an audit log).
- Rule for mixed data: implementations read the older major and write the newer one; a verifier accepts an audit log whose earlier entries carry an older major; mandates move to a newer format only by succession and are never rewritten.
- The vocabulary (Section 5.1) and the
$ids of the conformance schemas are versioned independently of the formats.
incompatible
typeof mandates, audit entries and checkpoints and the schema$idmoved fromhttps://mandate-spec.org/…tohttps://home-mandate.org/…, e.g.https://home-mandate.org/mandate/v0. Mandates with the old identifier are invalid (conformance casei127). Data model and evaluation are otherwise unchanged.- Because the
typeis part of every digest and signed payload, all digests, audit chains, checkpoint signatures and signed mandates inconformance/are new. Cases that are invalid on purpose keep their defect.
added
- Audit log: event
template.changedwith the membertemplaterecords changes to templates, the stored mandate drafts from which an implementation creates mandates (Section 2): stored, removed, hidden and shown again, with the name and digests but never the content. Eventapprover.changedwith the memberapproverrecords humans added to or removed from the implementation's list of approvers (Section 11.1). Both have an actor of kinduserorsystem;template.nameandapprover.idare displayed text, and aprevious_digestequal todigestmakes an entry invalid (Sections 9.1, 9.2). Compatible for logs: every entry that was valid remains valid. A verifier that does not know the events rejects a log that contains them (Section 14, unknown members). Conformance casesa58–a76inconformance/audit-v0.json. - Audit log: events
mandate.removedandagent.removedrecord that a revoked mandate or a revoked agent was removed from the implementation's lists, with the existing membersmandate(idand the digest of the last version) andagent; theiractoris of kinduserorsystem. Eventagent.reconnectedwith the memberagentrecords that a human gave new credentials to an existing agent that is not revoked, for example after an emergency stop; itsactoris of kinduser(Sections 9.1, 9.2, 11.3). Only revoked mandates and agents are removed; removal deletes no audit entries and keeps the protection against rollback (Section 3.5). Compatible for logs: every entry that was valid remains valid. A verifier that does not know the events rejects a log that contains them (Section 14, unknown members). Conformance casesa77–a88inconformance/audit-v0.json. - Approval requests that end without an answer before their timeout: new outcome
cancelledwith the memberapproval.cause(withdrawnby the agent,revoked,emergency_stop,interruptedby the end of the process), only withresult.status: deniedand thedenied_bymatching the cause; nobody answered, so noby(Sections 9.1, 11.1 items 8 and 9). A request that a revocation or an emergency stop ends is recorded with this outcome instead of without anapprovalobject. After a restart no waiting request is executed, reopened or answered; an execution whose effect is unknown is not repeated (failed, RECOMMENDEDerror: outcome_unknown). New RECOMMENDED codesalready_in_stateandstate_changedfor a confirmed action that is not executed because the state shown to the human no longer holds (Section 11.1 item 10). Compatible for logs: every entry that was valid remains valid. A verifier that does not know the outcome rejects a log that contains it (Section 14, unknown members). Conformance casesa89–a105inconformance/audit-v0.json.
Actions the resource cannot carry out (Section 11.1 item 11): an implementation that knows
that a resource cannot carry out the action SHOULD NOT ask anyone for it; the entry is
failed with the new RECOMMENDED code not_supported and without an approval. Which
actions the resource can carry out is told only to an agent that may read it (Section 13,
new item on features). No change
to the evaluation, to the vocabulary or to the format of mandates and logs. The profile for
Home Assistant names where the features of an entity come from.
Conformance cases (no change to any rule or format):
- Areas:
c193–c199(an area of 64 characters, the edge characters!and~, U+007F, non-ASCII, an empty area,Homenext tohome, a set in another order); two rules added toconformance/mandates/areas.json. - Parameters:
c200–c209,c212,c213(2^53 and 2^53 − 1, the spelling5e1, the values ofmodethatone_ofdoes not list, a constraint beyond the range of the vocabulary),d45–d48andi149(one_ofwith 32 values, a string value of 64 characters,min/maxbeyond the range,one_offormodein a rule without category). - Audit mode:
c210,c211(new mandateconformance/mandates/audit-mode-critical.json: adenyrule on a resource marked critical),s20(selection; the selection cases acceptenforcedas the evaluation cases do),v13,v14(succession switching audit mode on and off),a127–a129. - Selection with a set of areas:
s21. Audit log:a126(not_supported, Section 11.1 item 11),a130–a133(16 and 0 areas, a fraction and an empty string as parameter).
What request records (Section 9.1, text only, no change to any rule or format):
requestrecords the input to the evaluation as the PEP determined it, also when the evaluation found it invalid (invalid_request); identifiers in it are limited in length and number, not in their characters, as the schema has always had it.- Input that the log cannot record (a longer identifier, more than 16 areas or areas that
repeat, more than 16 parameters, a parameter value that is neither an integer nor a
string of the form of Section 4.5) is not a request: the PEP rejects it at its interface
before any evaluation. Conformance cases
a134(valid, an identifier with a space) anda135(invalid, anentity_idof 256 characters).
Audit mode and ask (Sections 4, 4.1, 4.4, 4.6, 6, 8, 9.1, 10, 11.1, 11.6, 12, 13; issue #59):
- In audit mode, a
denyrule that wins over a matchingaskrule no longer takes the confirmation away. Thedenystays the decision and is not enforced, but the result requires a confirmation: it carriesconfirm: trueand the approval settings of theask(Section 4.1), and the PEP executes it only after a confirmation, as anask(Sections 4.6, 11.1, 11.6). Before, such a request was executed without asking anyone, while the same request without thedenyrule needed a confirmation. Adenywithout a matchingaskrule is executed without a confirmation as before; critical actions are enforced as before. - New member
confirm(onlytrue, only withenforced: falseandreason: rule) of the result of the evaluation, of the AuthZEN response context (Section 6, REQUIRED there together withapproval_timeoutandapprovers), of theevaluateandselectresponses of the test interface and of the conformance cases (Sections 8, 10), and ofevaluationin the audit log (Section 9.1). A PEP that executes adenywithenforced: falseMUST knowconfirm; one that does not knowenforcedstill enforces everydeny. Implementations that already offer audit mode MUST addconfirm, or they execute such a request without asking anyone. - Audit entry: an
approvalobject appears withevaluation.decision: askor withevaluation.confirm: true. Withconfirm: true,executedrequiresapproval.outcome: approved, and theresultcan be anything anaskcan have; without it, adenywithenforced: falseis still onlyexecutedorfailed. Every entry that was valid stays valid. - Before the execution (Section 11.1 item 6), a confirmed request with
confirm: trueis executed only if the evaluation then givesallow,askor again adenywithconfirm: true, so that a time window that has closed prevails as for anask; a confirmedaskis still not executed after anydeny. In the audit log,denied_by: mandatewithconfirm: truerequires an approved or a revoked request. The human who confirms SHOULD be shown that the mandate in audit mode would deny the request, and by which rule (Section 11.1 item 2); the agent is not told. - Conformance cases
c214–c223(new mandateconformance/mandates/audit-mode-ask.json),s22(the selection cases acceptapproval_timeoutandconfirm),a136(valid: every outcome of an approval request withconfirm: true) anda137–a145(invalid). - Reference evaluator (Go):
Result.Confirm;Result.Approvalis also set with it.
Recording before executing, and the agent's values in the log (Sections 9, 9.1, 9.2, 11.1, 12.2, 12.4, 13; issues #86, #90):
- A PEP stores what it will record before it dispatches a command, and does not execute
when it cannot: new value
auditofresult.denied_by, for a request that was not executed because the log could not be written. Before, nothing said that recording comes first; a full disk or a crash between the command and the entry could execute anallowwithout a trace, although Section 9 promises a log that shows which agent did what.denied_by: auditappears only where no other denial came first: authentication, the emergency stop and the rate limit precede it; an answer other thanapprovedis still denied by the approval, an enforceddenyby the mandate; an unenforceddeny(audit mode) can now end so as well.outcome_unknownafter a restart now covers every action, not only confirmed ones. Section 9 says what durable means and what becomes of denials the PEP could not record yet. Compatible for writers of logs; a verifier of an earlier pre-release rejects an entry with the new value (beforev0.1.0). - The values of
requestcome from the agent and from the directory and may contain any character; whatever renders them for a human MUST treat them as untrusted text and show every character outside printable ASCII, and every backslash, as a JSON string shows them, never interpret them. Exports carry them unchanged. Text only: the schema already permitted the characters and Section 9.1 said so, but not what follows for interfaces, verifier reports and readers of exports. Conformance cases record such values. The reference verifier prints no value of an entry. - Conformance cases
a203–a212.
Request parameters and vocabulary v0.1 (Sections 3.1, 4, 4.5, 5, 5.1, 9.1, 9.2, 10.2,
12.4, 14; issues #78, #84):
- A request carries exactly the parameters its action declares: a parameter the action
does not declare makes it invalid (
c120,c160and the unit tests change), as do more than 16 parameters, a name outside^[a-z][a-z0-9_]{0,63}$, a missing required parameter, and none or more than one of the alternatives the action requires one of. The vocabulary file gainsrequiredper parameter andrequired_one_ofper action, the table in Section 5 a column. Section 12.4 makes forwarding only what was evaluated a MUST. Incompatible for the classevaluator(beforev0.1.0):c117–c120,c122,c138,c139,c158andc160areinvalid_requestnow;c10,c12,c28–c30,c47–c49carry the parameter their action requires. - Vocabulary
v0.1:alarm.armtakes the requiredmode(home,away,night,vacation,custom_bypass);climate.set_temperaturetakes a range setpoint,temperature_lowwithtemperature_highor the pair in degrees Fahrenheit, as one of its alternatives;mode,positionandvolumeare required,brightnessstays optional until the vocabulary names what elselight.setsets (#29). Section 14 now counts a new parameter that is not required as compatible and a newly required one as incompatible. - Sections 9.1 and 9.2 name the parameter names among the input the log cannot record, and
say that such input is not a request and gets no entry; the
invalid_requestresults exist so that an evaluator fails closed. Section 3.1 item 10 keeps constraints offdenyandaskrules and says why that still holds with required parameters. - No mandate changes validity: constraints on the new parameters become possible, nothing
becomes invalid.
decisionentries recorded under vocabularyv0keep their result; the version they name decides (Section 9.2). - Conformance cases
c224–c242, new mandateconformance/mandates/parameters.json.
Acceptance and selection (Sections 3, 3.4, 3.5, 4.1, 4.3, 6, 7.1, 8, 8.1, 9.1, 9.2, 10.2, 11.3, 11.6, 12.2; issues #76, #79, #80, #81):
- Selection (#76): an agent whose only mandates are revoked is denied with
revoked, notno_mandate; the result names the revoked mandate if it is exactly one valid one. An invalid stored document is a candidate for the agent and principal it names, or for every agent and principal if these cannot be read. Section 4.3 says how a stored document becomes invalid and that the household is shown such documents. The classpdpruns the evaluation cases whose mandate is not revoked; itsrevokedis tested by the selection cases. Incompatible for the classesselectionandpdp(beforev0.1.0):s07changes. - Succession (#79): the stored mandate of the succession rule includes one that is remembered after revocation, removal or expiry; the highest version is updated when a mandate takes effect, and a later successor replaces a waiting proposal.
- Accepted is not in effect (#80): a successor that does not merely narrow the stored
mandate (defined in Section 3.5), one that follows a revoked mandate, and a signed mandate
with
approversthe implementation does not know take effect only after a human confirmed them, as audit mode did. Thesuccessionoperation of the process binding answersconfirm: truefor such a successor; cases carrystored_revokedandconfirm. The reference code offersNeedsConfirmationandNarrows. - Signed mandates (#81):
audience, an optional member of the mandate with the form ofagent.client_id, required in a signed mandate together withexpires; a verifier accepts a signed mandate only for its own audience, never fetches keys from a location a mandate names, and withdraws the effect of mandates accepted by a key it stops trusting. Incompatible for the classsignatures(beforev0.1.0): the signed cases are regenerated from a mandate withaudienceandexpires; a mandate that usesaudienceis invalid for an implementation of an earlier pre-release (Section 14, unknown members). - Conformance cases
s07(changed),s24–s28,v01andv09(the offered mandate keeps the rate limit),v13(confirm),v15–v23,g01–g21(regenerated),g22,g23,d51,i156,i157.
AuthZEN mapping completed, optional mandate members, signed checkpoint time, further audit events, EdDSA canonicality (Sections 3, 3.1, 4.1, 4.3, 6, 7.1, 9.1, 9.2, 9.5, 10.2, 10.3, 11.4, 12.2; issues #77, #82, #83, #93):
- AuthZEN (#77):
resource.properties.criticalandcontext.timezonecarry the critical marking and the household time zone to a PDP that does not hold the directory; the response context carriesmandate_idnext tomandate_digest; a subject whosetypeis notagent, whoseidis missing or whoseprincipaldoes not match the pattern isinvalid_requestbefore any mandate is selected, also in theselectoperation of the process binding. The test tool comparesmandate_id. Incompatible for the classesselectionandpdp(beforev0.1.0): such a subject wasno_mandate. - Mandate (#82): optional
name(1 to 64 code points) and per rulenote(1 to 200), both displayed text, not part of the evaluation. A mandate that uses them is invalid for an implementation of an earlier pre-release (Section 14, unknown members). - Checkpoint (#82): the signed payload gains
at, therecorded_atof the checkpoint entry. Incompatible for logs (beforev0.1.0): checkpoints signed before do not verify. - Audit log (#83): events
issuer.changed(memberissuer:trusted,untrusted,key_added,key_removed, withkidfor the keys) andconfig.changed(memberconfig:timezone,clock_check,checkpoint_key, withvalueandprevious_value). Compatible for logs: only entries that were invalid become valid; a verifier that does not know the events rejects a log that contains them. The reference verifier checks thatprevious_valuediffers fromvalue. - Signatures (#93): for EdDSA,
S<Land canonical encodings ofRand of the public key;algmust match the key type. The text states what the reference already did; the reference now also rejects an Ed25519 public key that is not canonically encoded. - Conformance cases
d50,i152–i155,s23,g19–g21,a194–a202;a39–a44,a46–a49anda147re-signed with the time in the payload; the selection cases that select no mandate now sayselected: null, so that the HTTP binding checks thatmandate_idis absent.
Proposals name the version they would replace (Sections 9.1, 9.2, 11.6; issue #53):
mandate.previous_digestis allowed withmandate.proposedandmandate.discarded: the digest of the version the implementation held when it recorded the proposal, present whenever there was one; amandate.discardedrepeats it. A proposal named only the proposed version, so a reader had to search the log for the version in force at the time.- Section 11.6 item 4 now requires (MUST) an implementation that offers audit mode to record
proposals and discards, as the table in Section 9.2 already did, and says how the
confirmation is linked to the proposal: the
mandate.updated(ormandate.created, if no version was stored) carries the samedigestandprevious_digest; no member of its own. - Compatible for logs: only entries that were invalid become valid.
- Conformance cases
a190–a193;a157reworded.
added audit events (Sections 3.5, 4.4, 7.1, 9.1, 9.2, 11.4, 11.6, 13; issues #53, #73):
log.exported, with optionalexported(from_seq,to_seq): Section 13 asked for exports to be recorded, but no event existed for it.directory.changegainsadded,removed,category_changed(previous_category,category) andareas_changed(previous_areas,areas). A resource that enters the scope of a rule, a category that changes the vocabulary that applies and a set of areas that changes which rules match were not recordable.mandate.proposedandmandate.discarded(withmandate.cause): a mandate that would switch audit mode on and waits for a confirmation, and a proposal that does not take effect. Only the moment it took effect was recorded (#53). Added toaudit/v0now rather than to a later audit version: after the freeze, every new event needs a newtype.mandate.rejected(withmandate.cause, andmandate.idanddigestas far as the document could be read): a mandate from outside that was not accepted. The protection against rollback and forged signatures left no trace in the log.request.group, optional: connects thedecisionentries of one request for several resources.- Compatible: only entries that were invalid become valid; a verifier that does not know an
event or a member rejects a log that contains it (Section 14, unknown members). The
reference verifier checks
from_seq≤to_seq<seq, and thatcategoryandareasdiffer from their former values. - Conformance cases
a170–a189.
The vocabulary is bound to the formats (Sections 5, 5.1, 9.1, 9.2, 12.4, 14; issue #71):
- The vocabulary
https://home-mandate.org/vocabulary/v0belongs to the v0 formats, and the freeze covers it: fromv0.1.0, a new version under thisidonly adds (a new category, a new action, a new permitted value, a wider range). Removing or renaming a category or an action, changing a critical marking or narrowing a parameter needs a newidwith a new major of the formats. Before, Section 14 said only that the vocabulary is versioned independently and never changes under the sameidandversion; nothing said what a new version may change, although a removed action would have made valid mandates invalid and a removed marking would have run an action without its confirmation. No change to any document:vocabulary/v0.jsonis unchanged and itsversionstaysv0. - An implementation evaluates and verifies with the newest vocabulary version it knows.
Until
v0.1.0, the vocabulary can still change incompatibly, like the formats. - Audit entry: new optional member
vocabulary(id,version) of adecisionentry withevaluation, the vocabulary the evaluation used. For a category of Section 5 theidis that of v0, for an extension category that of the extension; the reference verifier checks it, and with the vocabularies of extensions it checks theiridtoo (an extension vocabulary withoutidis now rejected). Absent withunknown_resourceandunknown_category. Compatible: only entries that were invalid become valid; a verifier that does not know the member rejects a log that contains it (Section 14, unknown members). Implementations SHOULD record it, so that a decision stays reproducible after a vocabulary update. - Text only:
"*"includes the actions a later version adds (Section 12.4); extensions SHOULD follow the same rule (Section 5.1). - Conformance cases
a161–a169.
Schemas and formats in detail (Sections 3.1, 3.3, 3.4, 5.1, 9.1):
- Audit entry:
emergency_stop.releasedhas anactorof kinduser;mandate.createdandmandate.updatedhave one of kinduserorsystem. The text already said that only a human releases the emergency stop and that the human chooses the mandate; the schema permittedagent. Incompatible for logs (beforev0.1.0). Agents may still revoke their own mandate or themselves and activate the emergency stop. - Audit entry:
mandate.previous_digestappears only withmandate.updated(later also withmandate.proposedandmandate.discarded, see "Proposals name the version they would replace" above) and differs fromdigest, as for templates. Incompatible for logs (beforev0.1.0). - Audit entry:
evaluation.approval_timeoutis required foraskand withconfirm, and absent otherwise. Section 9.2 already required the entry to match the evaluation; the schema did not check it. Incompatible for logs (beforev0.1.0). - Identifiers of humans have up to 128 characters everywhere:
approversin a mandate,approval.byandapprover.idin the audit entry had 64, whilecreated_byandactor.idhad 128. Compatible: only documents that were invalid become valid. - An extension category in a mandate has at most 32 characters before and after the colon,
as in a vocabulary file. Incompatible for mandates (before
v0.1.0): a rule for a longer category could never match. - Text only: the size limit of a mandate counts the document as received, before
canonicalization, as the reference code always did; the forms of
agent.client_idandissuername the scheme length and the query the schema permits, and that percent-encoded dots are not dot segments;approval.bywithtimeoutnames an approver whose answer came too late. - Conformance cases
a153–a160,i150,i151,d49.
Verification of audit logs (Sections 8.1, 9.4, 9.5):
broken_atis determined condition by condition: a schema violation anywhere in the log comes before a wrong start or a broken chain, even if the chain breaks at an earlier entry. Before, the text also called it "the first entry in file order", which allowed two readings; the reference verifier has always reported the schema violation.broken_atis0for an entry without aseqthat can be read as an integer, andanchoredis0for a log without a verified checkpoint, as the conformance cases already expected.- An entry is at most 65 536 bytes, as the reference verifier already required; a line of white space only is an entry that violates the schema like an empty line.
- New rule: the
up_to_seqof alog.truncatedentry is smaller than the entry's ownseq. Incompatible for logs (beforev0.1.0): an entry that names itself or a later entry as deleted made the log invalid nowhere before; no implementation writes one. - All checkpoints of a log carry the same
log_idalso when no keys are given; the classauditcovers this check, as the reference verifier already did. - Conformance cases
a146–a152inconformance/audit-v0.json.
Conformance report (Section 10.4, no change to any rule or format; issue #65):
implementation.versionis the release of the implementation, not the version of the formats. Before, the reference harness reportedv0; it now reports its release.- Over the HTTP binding the test tool takes
implementationfrom the new options-nameand-version; before, it stayed empty, because the HTTP binding has nocapabilities.
History as mandate-spec
In the specificationincompatible mandates that were valid before can become invalid, and an invalid mandate denies every request. Implementations SHOULD check their stored mandates before they update:
agent.client_id(new Section 3.3): https URLs need a lower-case host and a path and must not contain userinfo, a fragment, white space or non-ASCII characters; no dot segments. Newly permitted: other URIs such asdid:…,spiffe://…,urn:…. Identifiers of the form<namespace>:<id>remain valid.- Timestamps: at most 9 fractional digits; the year
0000and the offset-00:00are invalid (Section 3.1 item 0). entity_idandareaare opaque identifiers (new Section 3.4): printable ASCII without space, up to 255 and 64 characters, compared exactly. Every identifier that was valid before remains valid. The evaluation no longer rejects a request because of the form of an identifier; the PEP MUST resolve the requested resource in its directory (Section 4).- A resource without category is
denywith the new reason codeunknown_resourceinstead ofinvalid_request(Section 4.1). - A rule without category may only use actions that some category of the vocabulary has (Section 3.1 item 4); before, such actions were not checked.
scene.activateis critical (Section 5): anallowrule withoutallow_criticalnow yieldsask.allow_criticaltogether with"*"is invalid (Section 3.1 item 9).- "No mandate for the agent and principal" is
denywith the new reason codeno_mandateinstead ofinvalid_mandate; several mandates at the same time areambiguous_mandate(Sections 4.1, 4.3 and 6). - Audit log:
denied_byonly withdenied, noerrorwithexecuted;evaluation.reasonmust belong toevaluation.decision;log.truncatedonly by a user or the system (Section 9.1). - Displayed text (Section 3.1 item 8): fixed code point list
data/forbidden-codepoints-v0.jsoninstead of the Unicode categories of the runtime; additionally forbidden are private-use and invisible (default ignorable) characters and characters, blank characters, noncharacters, every white space other than U+0020, a space at the start or end and a combining mark at the start; newly permitted are ZWNJ and ZWJ between other characters. The rule now also applies toactor.id,agent.display_nameandapproval.byin the audit log.
clarified each with new conformance cases:
- Patterns apply to the entire string and use only a portable subset of regular expressions (Section 3).
approval.timeout: hours are permitted (PT1H); at most 5 digits per component (Section 3.1 item 7).- Numbers are read by their value: digest of a mandate (Section 3.2) and
seqin the audit log (Section 9.1). - Time zones: links of the time zone database such as
US/Easternare permitted (Section 4.2). - Exchange format of the audit log: line feed as the only separator, no byte order mark, no
empty lines (Section 9.4).
conformance/audit-v0.jsonhas the new fieldjsonlfor it.
added
- Conformance classes (Section 8.1):
evaluator,selection,signatures,audit,audit-anchored,pdp. - Test interface (Section 10) with a process binding (JSON lines over standard input and
output, no network) and an HTTP binding for PDPs; test tool
cmd/mandate-conformancewith a machine-readable report andcmd/mandate-harnessas the harness of the reference code. - Sections 12 to 14: security considerations with the attackers and assumptions, privacy considerations, versions and compatibility. More requirements are stated with the key words of BCP 14.
- Mandates can carry
issuerandversion; succession protects against rollback (Section 3.5,conformance/succession-v0.json). - Signed mandates: compact JWS over the canonical form with EdDSA or ES256, with exactly
one accepted spelling; Section 7 is normative now (
conformance/signed-v0.json). - Checkpoints anchor the audit log: event
log.checkpointwith alog_idand a detached JWS over the position and digest of the log (Section 9.5); verification with keys reports up to whichseqthe log is anchored. - Section 11, obligations of the PEP: approval (binding, single use, independence from the agent, re-evaluation, limits), rate limit (at most N requests in every period of 3600 seconds, every request counts), revocation and emergency stop, clock.
- AuthZEN:
approversin the response context forask; trust between PEP and PDP (Section 6). - Audit log:
result.countfor combined rate limit denials; verification reports the number of entries; Section 9.4 states what the hash chain proves and what not. - Critical resources: the resource directory can mark a resource as critical; then every
action except
readis critical (Section 4, step 5). New optional inputcriticalof the evaluation, in the conformance cases and inrequest.resourceof the audit log. - Parameters and constraints (Section 4.5, Section 3.1 item 10): an
allowrule can limit integer parameters of an action (temperature,position,volume,brightness); the vocabulary names the parameters and their units. New optional inputparametersof the evaluation, in the conformance cases, inaction.propertiesof the AuthZEN request and inrequest.parametersof the audit log. - Selection of the mandate (Section 4.3) with
conformance/selection-v0.json; requests for several resources (Section 4.4). vocabulary/v0.json: the vocabulary of Section 5 as a normative file, withschema/vocabulary-v0.schema.json; the same format for extensions (Section 5.1).profiles/: informative mappings of platforms to the vocabulary. The Home Assistant and Matter columns left the table in Section 5.- Conformance cases with identifiers in the style of several platforms
(
conformance/mandates/identifiers.json). - Reference code:
displaytext.Check,evaluator.Approval.Duration,evaluator.SelectAndEvaluate,evaluator.Resource.Critical,evaluator.Request.Parameters, packageratelimit(a limiter that meets the bound of Section 11.2),audit.VerifyJSONLinesreads the log as a stream, packagejws,evaluator.Sign,evaluator.ParseSigned,evaluator.CheckSuccessor,audit.SignCheckpoint,audit.VerifyAnchored. conformance/schema/: JSON Schemas of the conformance files;conformance/manifest.json: all machine-readable files with SHA-256 and number of cases (Section 8).LICENSE(Apache 2.0),LICENSE-docs(CC BY 4.0),SECURITY.md,CONTRIBUTING.md.- Audit log: optional
approval.via, the channel an answer came through (Section 9.1), so that a log shows whether a person confirmed on a phone or in a user interface. Conformance casesa11–a13inconformance/audit-v0.json. - Reference evaluator:
IsCriticalexposes the critical actions of the vocabulary (Section 5); anything outside the vocabulary counts as critical. - Audit log: event
directory.changedwith the memberdirectoryrecords changes to the resource directory that affect the evaluation: the critical marking, renamed resources and how a human resolved a rename (Sections 9.1, 9.2 and 11.4). Required for implementations that let humans change the critical marking or track renamed resources. Compatible for logs: every entry that was valid remains valid. A verifier that does not know the event rejects a log that contains it (Section 14, unknown members). Conformance casesa50–a57inconformance/audit-v0.json. - Examples in English:
examples/*.jsonuse English display names, device identifiers and areas, and so do the conformance cases on them. Their digests change:d01–d03,d06andd08inconformance/digest-v0.json. The audit vectors keep their mandate digest, which is only data there.
incompatible
typeand schema$idmoved to the neutral domain:https://mandate-spec.org/mandate/v0. Name of the specification: mandate-spec.- New validity rules for mandates (Section 3.1):
format: date-timeis validated, no leap seconds, I-JSON, unique rule IDs,time_windowwith distinct start and end, actions matching the category, at most 256 KiB,expiresaftervalid_from, approval timeout 10 s to 1 h, no control or format characters in displayed texts. agent.client_id: https URL or<namespace>:<id>instead of the product-specific prefixhm-client:(existinghm-client:IDs remain valid).- Request: the entity ID is required and must match the pattern, as must the area; the status
of the mandate is required (
activeorrevoked). - The schemas are normative.
clarified (Section 4), each with new conformance cases:
- Pre-check: unknown category, action outside the vocabulary, invalid time specification
or invalid mandate →
deny. - Validity period
valid_from ≤ t < expires; revocation as an input to the evaluation. - Local time via the household time zone; time window start inclusive, end exclusive; weekday of the point in time; behavior during daylight saving time transitions.
rule_idand approval settings in the result (Section 4.1).limitsare not part of the evaluation.- Extensions without a known vocabulary →
deny. - Origin of inputs: category, area, time, time zone and status come from the PEP, never from the agent; comparison exact per character and case-sensitive.
- Time zones: only
UTCor IANA identifiers of the formArea/Location; seconds are truncated. - AuthZEN mapping of the fields (Section 6); the Home Assistant and Matter columns in the vocabulary are informative.
- Audit log: required fields per event,
executedonly after a permitting decision,broken_atalso on schema violations (Section 9).
added
- Digest of a mandate (Section 3.2), reason codes (Section 4.1), both required in the AuthZEN response context (Section 6).
- Audit log with a fixed format and hash chain (Section 9,
schema/audit-v0.schema.json). - Schema: time fields additionally with
pattern.
Conformance cases: new fields reason (required), timezone, revoked, rule_id,
approval_timeout; new files conformance/invalid-v0.json, conformance/digest-v0.json,
conformance/audit-v0.json; test mandates under conformance/mandates/.