nomos.can() – authority as a first-class primitive
Software has primitives for most of the hard things. Identity: you call an auth library. Data: you call a database. Network: you call fetch(). But "is this action permitted, and on whose authority" has no primitive. It lives as scattered if-statements, a hand-maintained rules file, a wiki page, a Slack thread. It isn't addressable, it isn't portable, and you can't hand the answer to anyone who doesn't run your code. nomos.can() makes it one call: import { can } from "@nomosprotocol/sdk"; const r = await can({ authority: "eu-ai-act", action: "deploy_system", facts: { risk_tier: "high", conformity_assessment: false }, }); // r.verdict -> "DENIED" // r.matched_rule_id, r.obligations, r.transcript_url The authority is a named, addressable thing, like a hostname, not logic you copy between services. You point can() at one of three kinds: - authority: "" -- a published authority anyone can query. Keyless for open ones. - artifact_id -- one you published yourself. - artifact + key_certs + root_public_key_pem -- you carry the authority's definition and a certificate chain, and can() verifies it offline against a root you pin. No default root, ever. Nothing calls home. One return shape for all three: a verdict (AUTHORIZED / DENIED / ESCALATED), obligations, and a signed transcript. The transcript is what makes it a primitive and not just a function: the answer is a value. Someone who doesn't trust your logs can verify the exact question and the exact answer, offline, with a public key and a short zero-dependency script. Two failure classes that are never merged: - DENIED -- the authority's rules say no. - NomosIssuerNotTrustedError -- can() could not establish who defined this authority. Different problems, different fixes. A better certificate chain fixes the second and never the first. An authority's definition is a sealed file (Ed25519 over a JCS/SHA-256 canonicalization). It can be revoked: a signed, dated list at a well-known URL, checked before every answer, surfaced as revoked + reason on the result. There's also an MCP gateway that runs every tools/call through the same can() check before it reaches the server: allow forwards, deny blocks with the matched rule, fail-closed if the service is unreachable. What's not built: - can() can't infer which authority applies from the facts. You name it or carry it. - The chain-of-trust format is published as a Draft, on purpose: one implementation exists (this one), and it shouldn't be called a standard until someone builds against the spec cold. - Who runs a real root is unsolved. npm: u/nomosprotocol/sdk. I'd like to hear where the model breaks.