MHS-COREInformativePart 2 of 2

OpenMHS Core

OpenMHS 0.9 development draft (`spec_version` `0.9`, wire revision `mhs_version` `1.0`)Sections 7–1520 min read4,441 words

Part 2 of 2 · MHS-CORE continues from Part 1. The document is split only for page size; the requirements are continuous.

On this page 12 sections
  1. 7. Receipts, idempotency, revisions and long-running work
  2. 8. Modes and the rule against silent simulation
  3. 9. Discovery
  4. 10. The control surfaces
  5. 10.1 MCP projection (reference: mhs.mcp)
  6. 10.2 Command line (reference: mhs.cli)
  7. 10.3 Code files (reference: mhs.run)
  8. 11. Principals, authentication and arming
  9. 12. Strict and compatibility mode
  10. 13. Persistence
  11. 14. Transport rules that hold in every mode
  12. 15. Claims and honesty

7. Receipts, idempotency, revisions and long-running work

CORE-090: Every mutation carries a client-chosen command_id (a ULID). The idempotency scope is (endpoint_id, device_id, command_id) and the digest is input_digest = sha256(JCS({capability_id, resource, input})) (PROTO-036, OP-006). A retry with the same digest returns the retained receipt with replayed: true and does not run again (OP-003); another digest is a rejected receipt MHS.CONFLICT.COMMAND_ID_REUSE that leaves the retained receipt untouched (OP-004). A rejected attempt is never the command's recorded outcome (OP-005); only the command's own non-rejected receipt with the same digest settles it. A replay of a command that is still running returns the live state of the operation.

CORE-091: A receipt's status is accepted, running, completed, failed, rejected, cancelled or unknown. At most one execution ever runs for a command_id, and it has exactly one final receipt, completed, failed or cancelled: the retained outcome that replays return. A rejected receipt records a refusal before any effect; it is chained as evidence but is not the outcome, does not reserve the command_id (a corrected retry with the same id may run) and leaves the revision unchanged. accepted and running are not evidence. unknown is the answer when the outcome cannot be observed in time or the response is lost: the execution's final receipt reconciles it later (x-vendor.reconciles {receipt_id, status, mhs_code}), and where nothing can (an endpoint restart, CORE-172) the unknown receipt stays the outcome. A client MUST read state before acting on an unknown outcome and MUST NOT retry it blindly (OP-013, OP-014, SAFE-055). An endpoint MUST NOT report completed for an effect it did not observe.

CORE-092: Every final receipt, rejected ones included, is appended to the evidence chain before it is returned (PROTO-029, EVID-010, EVID-012). Receipts follow the signal rules of CORE-071; a failure in the endpoint's own post-call code is failed MHS.INTERNAL.ERROR with the exception type only. A mhs.command input with no canonical JSON form (an integer beyond 2^53−1) is MHS.CAPABILITY.INPUT_INVALID (details.reason not_canonical_json) and is never dispatched (the e-stop excepted: it latches with a placeholder digest). NaN and infinity in an output become null and are listed in x-vendor.non_finite_output.

CORE-093: The result of mhs.command, mhs.resource.write, mhs.restore, mhs.operations.get, mhs.operations.cancel, mhs.safety.estop and mhs.safety.reset is the receipt object itself, validated by receipt.schema.json, with no {"receipt": ...} wrapper. Method-specific data lives in output: the e-stop's output has estop, latched, latency_ms, latch_latency_ms, safe_state_achieved, safe_state and cancelled_operations (CORE-077), and the reset's has reset, estop, latched, cleared_interlocks and resumed (CORE-082), and reenable when the device declares one (CORE-086). Internal methods use the receipt capability_id values cap.device.configure.v1, cap.internal.restore.v1, cap.internal.safety_estop.v1, cap.internal.safety_reset.v1 and cap.internal.operation_cancel.v1 (PROTO-038).

CORE-095: The device revision advances only when the device's state hash changed. Reads, every rejected receipt, safety.status, evidence, operations.get and session calls never advance it. A catalogued read class never advances it. For any other outcome (completed, failed, cancelled, unknown) the revision advances by one when the state hash differs before and after the call, or when it cannot be computed (an unreadable or unanswered state read counts as a change: fail closed). Stop classes and achieved_by actions, e-stops, restores and completed resets advance it whenever they run; a failed RESET_REFUSED after bridge.reset() ran advances it too (CORE-081). This restates OP-007 to OP-009: one advance per command, never for a read.

CORE-096: expected_revision is an integer (a boolean is not a revision). It is compared with the live revision after the device lock is taken and once more when the operation is registered; a mismatch is a rejected receipt MHS.CONFLICT.REVISION with current_revision recomputed at refusal time, and a permit it had consumed is given back. Every rejected receipt reports the true current revision.

CORE-100: deadline_ms bounds only the wait for the response, counted from the request's ingress; it never cancels or shortens an execution. Execution is bounded by max_duration_ms, taken from the binding's constraints and else from its class, counted from dispatch; that bound, not the response deadline, is the bridge's budget. A long_running binding (constraints.long_running or the class flag) whose deadline passes answers the current non-terminal receipt: status running, operation_id, x-vendor {long_running, deadline_ms, max_duration_ms}; the command keeps running, bounded by max_duration_ms (600000 ms when neither binding nor class declares one, and strict mode refuses a long_running binding with no bound). Any other command whose deadline passes answers unknown with MHS.OUTCOME.TIMEOUT, retryable: false, details.reason deadline_exceeded or max_duration_exceeded, and keeps running. This is the single rule behind PROTO-011 and OP-010, and a refusal before dispatch is OP-012 (CORE-061).

CORE-101: When max_duration_ms ends the wait before the deadline does, a cancellable command is asked to stop (cancellation.reason max_duration_ms exceeded): if it stops within a 0.25 s grace the caller receives cancelled, otherwise unknown with max_duration_exceeded. After the caller has been answered, a timer does the same at the bound and records unknown for a long-running command whose caller holds only a running receipt (SAFE-017).

CORE-102: The unknown receipt is recorded under a claim before the worker's final receipt, which then carries x-vendor.reconciles and replaces the stored receipt, so operations.get and a replay return it. Until then operations.get, operations.list and a replay report the operation's live running receipt (x-vendor.unknown_receipt_id). A late Busy instead frees the command_id.

CORE-103: A blocking method needs an integer deadline_ms of at least 1 (PROTO-010) and the endpoint caps it at hello.limits.max_command_deadline_ms (600000). A read that exceeds its budget fails with a structured timeout; restore and safety.reset answer a truthful timeout or unknown carrying the pre-assigned command_id, and never report a late success. The e-stop is exempt (CORE-076).


8. Modes and the rule against silent simulation

CORE-110: A device serves exactly one mode (hardware, simulated, hybrid, replay) and never changes it at runtime (MODE-002, MODE-003). A session requests the device's own mode; a mismatch is MHS.SESSION.MODE_MISMATCH and is never resolved by falling back (SESS-002). The mode is reported by the manifest, hello, safety.status, the session and every receipt (MODE-004), and by the reference file and every surface that lists a device.

CORE-111: Nothing that is not the real device may present itself as the real device. A fake or simulator backend of a hardware driver is served as mode simulated under the id <hardware id>/<backend> with a fidelity contract (F0 for a fake) and honest gaps (FID-001, FID-041, FID-045); it never answers under the hardware id. The backend names are hardware, fake and sim, and only hardware drives the device. A bridge MUST NOT substitute a simulation when hardware is unavailable; the endpoint fails instead (FID-049). Evidence from a fake or simulator is contract_checked and never a device-level conformance claim (CORE-191).

CORE-112: A hybrid device declares per resource which side is truth (fidelity.authority, MODE-006). The model may not drive a hardware-authority resource of a hybrid device: the command is refused with MHS.SAFETY.ENVELOPE_VIOLATION (details.authority hardware) and only a catalogued read class may touch it. Hybrid safety follows the stricter side (FID-034).

CORE-113: A replay device is read-only: every command is a pre-effect rejected receipt MHS.MODE.READ_ONLY and replay endpoints advertise replay.seek (MODE-005, FID-022, FID-024). The twin rule and the fidelity contract are the Sim/Fidelity/Replay profile (profiles.md §3).


9. Discovery

CORE-120: An HTTP endpoint serves its descriptor at GET /.well-known/mhs.json (endpoint-descriptor.schema.json) without authentication; the descriptor advertises auth (["bearer"] when tokens are configured, else ["none"]) and the endpoint's honest transports and features (TPT-002). A stdio endpoint is found through its launch command.

CORE-121: A client finds endpoints from two configured sources: the environment variable MHS_ENDPOINTS (comma-separated: http://host:port, mhs://host:port, whose port defaults to 7400, and stdio:<command>, whose words are separated by spaces and which is listed but never launched by discovery) and the endpoints file ~/.mhs/endpoints.json (or $MHS_ENDPOINTS_FILE), validated against endpoints.schema.json. mhs discover probes every HTTP entry.

CORE-122: Discovered does not mean trusted. Discovery authenticates nothing: every entry is reported trusted: false, an invalid endpoints file is a usage error, and a discovered endpoint receives only the token_file of its own entry, never a token given for another endpoint. Two discovered endpoints that list one device id make that name ambiguous (MHS.HOST.DEVICE_AMBIGUOUS). mhs.registry.search is not an endpoint method; a registry result is never a controllable device before authentication (PROTO-039).


10. The control surfaces

The MCP projection, the mhs command line and code files are generated from the same reference file and are thin clients: each holds only the authority of the session it opens, and the endpoint enforces every limit, interlock, permit and e-stop rule whatever surface called.

10.1 MCP projection (reference: mhs.mcp)

CORE-130: The projection exposes one tool per binding, mhs__<device_slug>__<capability_slug> (TPT-030), and the fixed tools mhs__devices, mhs__inspect, mhs__read, mhs__safety, mhs__operations, mhs__evidence and mhs__estop. The tools mhs__reset and mhs__permit are listed and callable only when the operator enables operator_tools, in every mode; otherwise they refuse MHS.AUTH.FORBIDDEN (policy operator_tools=false). The host policy has four flags, enabled, allow_hardware, operator_tools and allow_external_sessions; without a policy document the projection is enabled and every other flag is false, and with one a flag is true only when it is the JSON value true. enabled false refuses every call with MHS.AUTH.FORBIDDEN; allow_hardware false refuses capability tools, mhs__read and mhs__operations on hardware devices with MHS.AUTH.HARDWARE_DISABLED, while discovery, mhs__safety, mhs__estop and mhs__evidence stay available.

CORE-131: tools/call is exactly one mhs.command. A receipt of any status is a successful call (isError false, structuredContent.receipt, the full receipt also in the first text block); isError is true only when no receipt exists: a policy refusal, a lapsed session, a transport failure, or an arguments object that does not match the input schema (MHS.CAPABILITY.INPUT_INVALID, nothing sent) (TPT-038, TPT-039). A value outside an enforced limit is sent and answered by a rejected receipt ENVELOPE_VIOLATION, kept in the evidence; the projection describes limits but the endpoint enforces them. The optional control fields are session_id, command_id, expected_revision, permit_id and deadline_ms (TPT-033).

CORE-132: The projection never trusts the model's session_id: it refuses one it did not open with MHS.SESSION.UNKNOWN unless allow_external_sessions is set. It opens its own sessions with the principal {kind: agent, id: agent:mcp/<clientInfo.name>}, sends a heartbeat before a call when less than a third of the TTL is left, and opens a new session after EXPIRED, UNKNOWN or CLOSED; reads are retried once on the new session, commands never. A _meta.retryToken, or a command_id that is not a ULID, maps deterministically to the ULID formed from the first 16 bytes of sha256(tool_name + "\n" + token), so a retry replays instead of acting twice (the mapping is scoped by tool name only, a known limitation; open item in the 0.9 change notes, MHS/changes/WP-09.md). mhs__estop, ping and notifications/cancelled are served on a priority path while a tools/call is in flight. Frames are at most 4 MiB, and the projection answers the requested MCP protocol version when it is one of 2024-11-05, 2025-03-26, 2025-06-18 and 2025-11-25, otherwise 2025-11-25.

CORE-133: A tool description is at most 2048 bytes and is generated from mhs.devices.get: mode, risk class, the enforced limits, the interlocks the call needs, the permit requirement, long-running behaviour and the facts of the reference file. Tools carry the annotations readOnlyHint (read verbs), destructiveHint (non-read calls on R2/R3 or hardware and hybrid devices), idempotentHint and openWorldHint (hardware and hybrid), and x-mhs with the capability metadata. Tool names are stable across restarts; a slug collision fails closed (TPT-032).

10.2 Command line (reference: mhs.cli)

CORE-136: The mhs command (python3 -m mhs, mhs, openmhs) provides: serve <dir|adapter.json|reference fixture> (--stdio, --port, --mcp, --backend fake|sim|hardware, --allow-hardware, --arm, --state-dir, --site-limits, --token-file, --operator-token-file), driver check|reference|new|export|catalog, validate, devices, describe (alias inspect), hello, read, write, call, session, estop, status, reset, evidence, run, discover, conform, registry and version. serve always loads in strict mode and defaults to the fake backend; only --backend hardware drives a device, and it needs --allow-hardware and --state-dir (CORE-154). --json is the machine contract; human output may change. estop needs no session and no mode and stops every device unless --device is given; reset needs the operator token file and an interactive terminal and sends nothing otherwise; describe prints mode, risk, trust, e-stop, safe state, interlocks, verbs, effective limits, fidelity gaps and facts.

CORE-137: Exit codes are one table for every command: 0 success (a completed receipt, or accepted/running work); 1 the command ran but was refused or failed (a rejected, failed, cancelled or unknown receipt, any other endpoint error such as MHS.DEVICE.UNKNOWN, an invalid document); 2 usage error; 3 transport error (the endpoint cannot be reached, or the response was lost: MHS.OUTCOME.TIMEOUT, MHS.OUTCOME.UNKNOWN); 4 authentication or authorisation (MHS.AUTH.*, a reset without an interactive operator, a hardware backend without --allow-hardware); 5 conform did not reach the required level; 130 interrupted. A receipt maps to 0 or 1 by its status whatever its error code, except a refusal whose code is MHS.AUTH.*, which is 4. A multi-device estop exits 0 only when every device has a completed receipt and otherwise with the code all failures share (3 or 4), else 1. TPT-052 states the same table.

10.3 Code files (reference: mhs.run)

CORE-140: A code file is a plain Python script run by mhs run. mhs.run.lab(*targets, check=True, ledger=True, mode=None, ttl_s=600, token=None, principal=None) connects to each endpoint once and gives every device its own handle and session, opened in the device's own mode. With check=True, the default, any receipt other than completed (or work still running) raises MHSRejected; this is the one exception to TPT-055, and check=False returns the receipts. A code file has no more authority than the same calls made one by one, never retries a physical command, and never resets a latch: bench.estop(reason) stops every device, tries all of them even if one fails, and raises with data["errors"] and data["receipts"]. token is sent only to the endpoints named in the call.

CORE-141: Every run writes a ledger, .mhs-run/<run id>.json (ledger_version 1, a ULID run_id, script_sha256, endpoints[], receipts[], lost[], outcome, error, exit_code, sdk). The ledger is checked against the endpoint's own evidence (mhs evidence --ledger, mhs.run.verify_ledger): the hash chain must verify and every ledger receipt of that endpoint must be in it with the same command id, device, capability and status; the check fails (valid: false) with chain_invalid, missing_receipts, mismatched_receipts, endpoint_not_in_ledger or no_receipts_for_endpoint.


11. Principals, authentication and arming

CORE-150: The principal of a request is the principal of its bearer token: auth_tokens maps a token to {id, kind} with kind human, agent, service or operator, compared in constant time against every token; on the wire an operator is kind: "human" with x-meta.role: "operator". session.open with a token records the token's principal (x-meta.authenticated: true, a different params.principal kept as x-meta.claimed_principal) and the session is usable only by requests with that principal; any other caller sees MHS.SESSION.UNKNOWN (the SSE stream: 403). The members x-meta.authenticated, role and claimed_principal belong to the endpoint: a caller's are dropped, and a principal without authenticated is unauthenticated. Without a token a strict endpoint records a self-declared human or operator as an agent with x-meta.claimed_principal; a compatibility endpoint keeps it and warns once per principal (SESS-007, SAFE-061).

CORE-151: An operation belongs to its principal (id, kind, authenticated). In strict mode operations.get, list and cancel hide another principal's operations as MHS.OPERATION.UNKNOWN (PROTO-031).

CORE-152: HTTP serves only loopback without tokens. A non-loopback bind (anything but localhost, 127.0.0.0/8 and ::1; wildcards included) without auth_tokens is refused with MHS.AUTH.TRANSPORT_INSECURE unless insecure_dev=True, which warns. Over stdio the process that holds the pipe is the principal (SAFE-060, TPT-013).

CORE-153: Hardware actuation, a non-read and non-stop command on a hardware device, needs the device to be armed; otherwise it is a rejected receipt MHS.AUTH.HARDWARE_DISABLED (details.reason not_armed) with no bridge call. Arming exempts reads and stop classes only; a safe_state.achieved_by action is exempt from the latch (CORE-075) but not from arming, and the e-stop path runs those actions itself whatever the arming. armed unset arms nothing in strict mode and every device (warned) in compatibility mode; an explicit False or a list of device ids holds in both. Arming is an operator decision (mhs serve --arm, MHSServer(armed=...)).

CORE-154: A hardware backend starts only on an explicit operator decision: allow_hardware, a state_dir (so the latch and the journal survive a restart), and, for a model-authored driver at R2 or above, a human reviewed_by (DRV-040). The loader refuses otherwise (LoadError.reason hardware_not_allowed, state_dir_required, review_required).


12. Strict and compatibility mode

CORE-160: MHSServer(strict=...) takes None or False (compatibility mode, the 0.9 default of the bare constructor; the default flips after the pilot) or True (strict). mhs.adapter.load, mhs.driver.build_server and mhs serve <dir> are always strict, and passing strict to load is a TypeError. A device is strict when the server or the device says so. The following hold in both modes: the e-stop and reset rules (CORE-074 to CORE-083, CORE-086, CORE-087 and CORE-088), element-wise limits and their intersection, every load-time limit error (CORE-043), LIMITS_UNSET for operator_required paths, full ULIDs, the HTTP rules (CORE-180 to CORE-185), schema.get of shipped schemas only, SSE ownership, session expiry, persistence, strict JSON decoding, refusal of a duplicate (capability_id, resource) pair, and the non-finite output rule. Strict-only: catalogue resolution of every binding (unknown ids, drift, KIND_MISMATCH, the risk floor, the class verb, the class requires_permit flag), binding ∩ class schemas, output validation, the no-limit form of LIMITS_UNSET, SAFE-045, the approval verifier for hardware permits at R2 and above, operator-only hardware reset, default-off arming, the downgrade of unauthenticated self-declared principals, and the hiding of other principals' operations.

CORE-161: Each static finding (unknown-capability, schema-drift, verb-differs, kind-mismatch, risk-floor, achieved-by-unbound, required-for-empty, required-for-unbound, interlock-invalid, limits-invalid, envelope-unbound, SAFE-045, the fidelity-* findings, constraints-invalid, ...) is a refusal in strict mode: construction raises MHSStrictError with every refusing finding (.findings, .codes, .rules). In compatibility mode each is an MHSStrictWarning listed in MHSServer.strict_findings, a risk class below the class floor is raised to the floor, a call to a drifting binding whose input the class schema refuses warns once per capability, and the 0.9 behaviour is otherwise kept. SAFE-045 covers hybrid devices as well as hardware devices. Endpoint-configuration findings (hardware-armed-implicitly, approval-verifier-missing, operator-token-missing) are never refusals.


13. Persistence

CORE-170: With MHSServer(state_dir=...) the endpoint keeps, in that directory, evidence.jsonl (the chain), latch.json (openmhs.state.latch/1), journal.jsonl (openmhs.state.journal/1: the idempotency index, write-ahead begin/end records, cumulative draw/release records and a header with the revisions reached) and a POSIX lock: one endpoint process uses a directory at a time. A directory that is in use, belongs to another endpoint, or whose evidence or journal cannot be trusted refuses the start (StateDirError). hello.limits.receipt_store is state_dir, or memory without one, and then the latch, the idempotency store and the revisions are lost on restart.

CORE-171: latch.json is replaced atomically and fsynced under the endpoint lock, before any bridge I/O, by every e-stop and every completed reset (SAFE-065). A failed write never stops the latch; it is reported in the e-stop's output.latch_persisted and in safety.status estop.persisted. On start a latched device stays latched, including one the endpoint does not serve now, and safety.status shows estop.restored: true and estop.confirmed: false until the next e-stop, or a replay of the latching e-stop's command_id, runs the stop path again. The latch after a restart is latch.json or the evidence (the last engaging e-stop receipt with no later completed reset); a missing latch file next to receipts, and a latch file that cannot be read, fail latched. In the unreadable case every served device and every device in the evidence starts latched, the bytes are kept as latch.json.unreadable-<ULID> and each device is released only through mhs.safety.reset.

CORE-172: A completed command id replays its receipt byte for byte after a restart, with zero bridge calls. A command that was in flight when the process died gets an unknown receipt (MHS.OUTCOME.UNKNOWN, retryable: false, details.reason endpoint_restarted, with lost_receipt_id when the journal kept a receipt the evidence lost) and is not run again (OP-006). An effectful command whose write-ahead record cannot be written is refused before any bridge I/O with MHS.INTERNAL.ERROR (details.reason journal_write_failed); reads, stop classes and safe-state actions are not.

CORE-173: Revisions continue above the highest any previous run reached, so no pre-restart revision is current again and a stale expected_revision is MHS.CONFLICT.REVISION. A final line of the evidence log that a crash cut short is moved aside (<path>.torn-<ULID>, warned); damage anywhere else fails verification and refuses the start.


14. Transport rules that hold in every mode

CORE-180: Every HTTP route checks the Host header: it must name localhost, 127.0.0.1, [::1], the bound address or an allowed_hosts entry (case-insensitive, port ignored, a missing header refused), else 403 MHS.AUTH.FORBIDDEN. An Origin header, when present, must be the endpoint's own loopback origin (same port) or an allowed_origins entry (null is refused), else 403.

CORE-181: With auth_tokens configured, every route except /.well-known/mhs.json needs Authorization: Bearer <token>, otherwise 401 MHS.AUTH.UNAUTHENTICATED with WWW-Authenticate: Bearer. The e-stop is not exempt, so every client is given a token; a token is never echoed (TPT-014).

CORE-182: POST /mhs/v1/rpc requires Content-Type: application/json (parameters allowed), otherwise 415 MHS.PROTOCOL.INVALID_REQUEST; GET on that path is 405 and never executes a method (TPT-011). Every early refusal closes the connection so an unread body is never parsed as the next request; every connection has a socket timeout (default 30 s); a request over 1 MiB is MHS.LIMIT.PAYLOAD_TOO_LARGE; a batch over 64 requests is MHS.PROTOCOL.BATCH_TOO_LARGE.

CORE-183: Transports decode JSON strictly (RFC 8259): a NaN or Infinity literal is MHS.PROTOCOL.PARSE_ERROR (HTTP 400), and so are non-finite values in in-process params and a number that overflows to infinity (1e999; over HTTP that case answers status 200 with the JSON-RPC error, a known inconsistency; open item in the 0.9 change notes, MHS/changes/WP-09.md), except in an e-stop request (CORE-076). A request for an effectful method (mhs.command, mhs.resource.write, mhs.restore, mhs.safety.reset, mhs.safety.permit, mhs.composition.run) sent as a notification is dropped and logged and never executed, because its outcome could not be receipted (PROTO-005); an e-stop notification still latches.

CORE-184: GET /mhs/v1/events?session_id=<id> requires a live session (missing id 400, unknown or expired 403, a token-bound session of another principal 403) and drains only that session's subscriptions; an optional subscription_id must belong to it (else 404). A principal may hold at most 4 streams (429 MHS.LIMIT.CONNECTION_LIMIT). Frames are id: <subscription>:<seq> and event: mhs.event after an initial retry: 3000 (TPT-016); mhs.unsubscribe works only for the owning session. Safety events (e-stop engaged or confirmed, reset) travel on the control stream with type safety.

CORE-185: mhs.schema.get serves only the classes the device binds and the schemas shipped with the package, resolved by bare file name or known $id; a path, .., ~, a drive or a URL is refused (INVALID_PARAMS, HTTP 404), and a document a local caller loaded is never served (PROTO-022).

CORE-186: mhs.hello returns server, implementation {name, version}, spec_version, mhs_version, protocol_versions, endpoint_id, time, started_at, features, limits, conformance, devices and safety {certified: false, notice}. limits has max_request_bytes (1048576), max_batch (64), max_subscriptions (64), max_rate_per_s (50), session_max_ttl_s (86400), receipt_retention_days and max_receipt_retention_s (the same retention in two units, default 30 days, EVID-018), receipt_store and max_command_deadline_ms (600000). conformance lists only the devices' own claims and is [] when none is made. The endpoint implements 24 methods: the 26 of 02 §3 less mhs.composition.run (reserved) and mhs.registry.search (not an endpoint method; MHS.PROTOCOL.METHOD_NOT_FOUND).


15. Claims and honesty

CORE-190: mhs.conformance.report returns the last report stored with record_conformance_report (its digest must recompute and its subject.id must be the endpoint or one of its devices) or, when none was stored, a schema-valid report in which every suite test is skip with detail not_run, level_achieved CL0, and the disclaimers not_certified and self_issued. An endpoint MUST NOT report a level it has not run.

CORE-191: A pass on a fake or simulator backend produces the evidence state contract_checked and never an MHS-Device level. A hardware evidence level comes only from an operator-run check on the real device. Reports, badges and registry records keep the wording of SAFE-052.