ag_id 0.1.0

Ag^id — deterministic identifiers that survive platform translation
Documentation
{
  "spec_version": "1",
  "comment": "Canonical Ag^id (ag_id) v1 test vectors. Any conforming implementation in any language MUST reproduce the positive vectors byte-for-byte AND reject the negative cases as specified. Inputs are given as ASCII strings; treat them as their UTF-8 byte sequence.",
  "protocol": {
    "prefix": "agid:v1:",
    "hash": "BLAKE3-256",
    "encoding": "base58 (Bitcoin alphabet, no 0/O/I/l)",
    "uri_prefix": "did:agid:"
  },
  "domain_bytes": {
    "User": "0x01",
    "Document": "0x02",
    "Session": "0x03",
    "Device": "0x04",
    "Concept": "0x05",
    "Opaque": "0x00 (reserved sentinel \u2014 never used to derive)"
  },
  "vectors": [
    {
      "name": "user_alice_at_example",
      "domain": "User",
      "domain_byte": "0x01",
      "input_utf8": "alice@example.com",
      "raw_hex": "1a2fdea0f9612c18e110ea43eb149d397f997a730d93ca2d8e1178835d9e1925",
      "did_string": "did:agid:2mDwJhrvWdJsqHAhRTQWpaLgWmnTZxEZJv6hnDmjiYtt"
    },
    {
      "name": "golden_v1_anchor",
      "comment": "Stability anchor \u2014 MUST match across all v1.x releases. The Rust integration test tests/determinism.rs::did__derive__golden_input__matches_hardcoded_hex pins this raw_hex value.",
      "domain": "User",
      "domain_byte": "0x01",
      "input_utf8": "agid:golden:v1",
      "raw_hex": "7c62f564159188295c2eb1b4fa3b67edb81c21b07b53d21b040f1a03340de849",
      "did_string": "did:agid:9NZ5Hr3TjTUMCZcx7yAUmVgvjKiXAYPGWk99jBsXqeTJ"
    },
    {
      "name": "empty_input_user",
      "comment": "Empty input is allowed and well-defined.",
      "domain": "User",
      "domain_byte": "0x01",
      "input_utf8": "",
      "raw_hex": "971734801ac0437067ac7f521b422a0e4619ef7e7ff2d15bb6adf5da0f7b7058",
      "did_string": "did:agid:BAo5w7gcSBMsLqYRqDEJ7pVuj5MQM8W567A5cWmyCUcf"
    },
    {
      "name": "unicode_cyrillic",
      "comment": "Hash is over UTF-8 bytes (\"\u041c\u0438\u0445\u0430\u0438\u043b\" \u2192 0xd09c d0b8 d185 d0b0 d0b8 d0bb in UTF-8). Encoding identity is the caller's responsibility.",
      "domain": "User",
      "domain_byte": "0x01",
      "input_utf8": "\u041c\u0438\u0445\u0430\u0438\u043b",
      "raw_hex": "38f12866493850cbe71cbe622551bda534945321f7ed3ebd27364b986911fcf5",
      "did_string": "did:agid:4qH7WfFHC1fcaczryBD9mHXC1Jc8PDYcWGmwdNvVr7QY"
    },
    {
      "name": "domain_separation_user",
      "comment": "Pair with `domain_separation_document` and `domain_separation_session` \u2014 same input, different domains, must produce different IDs.",
      "domain": "User",
      "domain_byte": "0x01",
      "input_utf8": "same-input",
      "raw_hex": "32bd40b0c58f71e094bb6f278fb39e6d01faa4826e59416fea77d9bdd71f5658",
      "did_string": "did:agid:4R4m8n9tdzRXdMEPV2pbyWwdnX8mDyavKo68A7etME5m"
    },
    {
      "name": "domain_separation_document",
      "domain": "Document",
      "domain_byte": "0x02",
      "input_utf8": "same-input",
      "raw_hex": "b8b7595855b3a25023ef241757c35b5913f2532889ac8e360f26d89efc4a2799",
      "did_string": "did:agid:DS4AvqeTHmgHxPAHKjX3ZBLKHYqkerj5gwSUwBbXXPKW"
    },
    {
      "name": "domain_separation_session",
      "domain": "Session",
      "domain_byte": "0x03",
      "input_utf8": "same-input",
      "raw_hex": "e256b85b39e02bd0b1251820b7bd4a295d757d302c2e1a86cc937b415a1201cd",
      "did_string": "did:agid:GEXqTNt4cvGMtwatn1y7KLyor19nLexU8UMGyvJ1QsQQ"
    },
    {
      "name": "custom_domain_0xff",
      "comment": "Custom domain with byte 0xFF.",
      "domain": "Custom",
      "domain_byte": "0xff",
      "input_utf8": "some-input",
      "raw_hex": "2eb5364a26c6dbee6b5716d18d5356d42b5e7b8644c481f277c54396fc7751a4",
      "did_string": "did:agid:49L26pAbHYxQpENhZT7qmMLCYJhmUeg2HaBJPsBuL2su"
    },
    {
      "name": "long_input_1024_zeros",
      "comment": "1024 bytes of 0x00. Verifies that BLAKE3 is fed the bytes verbatim with no length-prefix tampering.",
      "domain": "Document",
      "domain_byte": "0x02",
      "input_utf8": "(1024 bytes of 0x00 \u2014 see Rust generator tests/vector_export.rs)",
      "raw_hex": "b0f561357d041b2220fbf570abb85966081db915b2b6480b83b5844236edb810",
      "did_string": "did:agid:Cummes8JKskjxa73rshacP3DTdvFo3eweuWGYcqvaTaw"
    }
  ],
  "negative_cases": {
    "comment": "Conforming implementations MUST reject these inputs. Error names are the Rust reference's enum variants (ag_id::Error). Other-language implementations should use equivalent typed-error semantics.",
    "derivation_rejections": [
      {
        "name": "reserved_domain_zero",
        "comment": "Derivation with domain_byte = 0x00 is reserved for Opaque and must be rejected at construction. The Rust type system also prevents this at compile time (DeriveDomain has no Opaque variant and DeriveDomain::custom(0) returns Error::ReservedDomain).",
        "domain_byte": "0x00",
        "expected_error": "ReservedDomain"
      }
    ],
    "parse_rejections": [
      {
        "name": "missing_prefix_plain_string",
        "comment": "A string with no DID-shape at all.",
        "input_string": "not a did at all",
        "expected_error": "MissingPrefix"
      },
      {
        "name": "missing_prefix_wrong_method",
        "comment": "A DID URI for a different method.",
        "input_string": "did:other:3Yu9e55XiwG1UyLwh2ojPB2tUkZdJmpRv8gWf7jazngP",
        "expected_error": "MissingPrefix"
      },
      {
        "name": "missing_prefix_legacy_agf",
        "comment": "An unrelated DID method prefix MUST NOT be accepted as equivalent to `did:agid:`.",
        "input_string": "did:foo:3Yu9e55XiwG1UyLwh2ojPB2tUkZdJmpRv8gWf7jazngP",
        "expected_error": "MissingPrefix"
      },
      {
        "name": "missing_prefix_uppercase_method",
        "comment": "DID method names are case-sensitive per W3C DID Core 1.0 \u00a73.1; `DID:AGID:` is not the same as `did:agid:`.",
        "input_string": "DID:AGID:2mDwJhrvWdJsqHAhRTQWpaLgWmnTZxEZJv6hnDmjiYtt",
        "expected_error": "MissingPrefix"
      },
      {
        "name": "wrong_length_empty_payload",
        "comment": "Empty base58 payload after the prefix.",
        "input_string": "did:agid:",
        "expected_error": "WrongLength"
      },
      {
        "name": "wrong_length_payload_too_long",
        "comment": "Payload length exceeds 44 characters; cannot decode to exactly 32 bytes.",
        "input_string": "did:agid:2mDwJhrvWdJsqHAhRTQWpaLgWmnTZxEZJv6hnDmjiYttExtraTrailingCharacters",
        "expected_error": "WrongLength"
      },
      {
        "name": "invalid_base58_ambiguous_chars",
        "comment": "Contains characters explicitly excluded from the Bitcoin base58 alphabet (0, O, I, l).",
        "input_string": "did:agid:O0Il",
        "expected_error": "InvalidBase58"
      },
      {
        "name": "invalid_base58_underscore",
        "comment": "Underscore is not in the base58 alphabet.",
        "input_string": "did:agid:invalid_underscore_payload",
        "expected_error": "InvalidBase58"
      },
      {
        "name": "invalid_base58_non_ascii_payload",
        "comment": "Payload contains non-ASCII bytes; conforming parsers MUST reject before base58 decoding.",
        "input_string": "did:agid:\u041c\u0438\u0445\u0430\u0438\u043b",
        "expected_error": "InvalidBase58"
      },
      {
        "name": "malformed_did_partial_garbage",
        "comment": "Valid prefix immediately followed by punctuation outside the base58 alphabet.",
        "input_string": "did:agid:!!!",
        "expected_error": "InvalidBase58"
      }
    ]
  }
}