From bd9cf3787b39103e8859da9c6e0368df95e0fc75 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Thu, 9 Oct 2025 06:58:52 -0600 Subject: [PATCH 01/13] begin enforcing schema hygiene Signed-off-by: Daniel Hardman --- .github/workflows/check-schemas.yml | 22 ++++++++++ tools/.ignore_check_schemas | 1 + tools/check_schemas.py | 63 +++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 .github/workflows/check-schemas.yml create mode 100644 tools/.ignore_check_schemas create mode 100644 tools/check_schemas.py diff --git a/.github/workflows/check-schemas.yml b/.github/workflows/check-schemas.yml new file mode 100644 index 0000000..bce7fe2 --- /dev/null +++ b/.github/workflows/check-schemas.yml @@ -0,0 +1,22 @@ +name: Check Schemas + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + check-schemas: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Run check_schemas.py + run: | + python tools/check_schemas.py diff --git a/tools/.ignore_check_schemas b/tools/.ignore_check_schemas new file mode 100644 index 0000000..ffd7f70 --- /dev/null +++ b/tools/.ignore_check_schemas @@ -0,0 +1 @@ +'vLEI' is not lower-case kabob-case \ No newline at end of file diff --git a/tools/check_schemas.py b/tools/check_schemas.py new file mode 100644 index 0000000..d77ef19 --- /dev/null +++ b/tools/check_schemas.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +import sys +import os +import re + +IGNORE_FILE = os.path.join(os.path.dirname(__file__), ".ignore_check_schemas") +error_count = 0 +ignore_regexes = [] +folders = [] + +def load_ignore_regexes(): + global ignore_regexes + if os.path.exists(IGNORE_FILE): + with open(IGNORE_FILE, "r") as f: + ignore_regexes = [re.compile(line.strip()) for line in f if line.strip()] + else: + ignore_regexes = [] + +def report_error(msg): + global error_count + for regex in ignore_regexes: + if regex.search(msg): + return # Suppress error + print(msg, file=sys.stderr) + error_count += 1 + +def check_nothing(): + # Placeholder for a real check + pass + +# Helper to get repo root +def get_repo_root(): + return os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + +def populate_folders(): + global folders + repo_root = get_repo_root() + for name in os.listdir(repo_root): + path = os.path.join(repo_root, name) + if os.path.isdir(path) and not name.startswith(".") and name != "tools": + folders.append(name) + +def check_folder_names(): + kabob_case_re = re.compile(r'^[a-z0-9]+(-[a-z0-9]+)*$') + for folder in folders: + if not kabob_case_re.match(folder): + report_error(f"Folder name '{folder}' is not lower-case kabob-case.") + +def main(): + # Set working dir to repo root + os.chdir(get_repo_root()) + load_ignore_regexes() + populate_folders() + # Find all check_* functions + check_funcs = [v for k, v in sorted(globals().items()) if k.startswith("check_") and callable(v)] + for func in check_funcs: + func() + if error_count > 0: + sys.exit(1) + sys.exit(0) + +if __name__ == "__main__": + main() From dfd6431ad90f013ae1aa6f644d9b39f4df9be695 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Thu, 9 Oct 2025 08:15:04 -0600 Subject: [PATCH 02/13] guarantee jsonschema is available for gha Signed-off-by: Daniel Hardman --- tools/.ignore_check_schemas | 3 ++- tools/check_schemas.py | 25 ++++++++++++++++++++++++- tools/requirements.txt | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tools/.ignore_check_schemas b/tools/.ignore_check_schemas index ffd7f70..ef0c0d8 100644 --- a/tools/.ignore_check_schemas +++ b/tools/.ignore_check_schemas @@ -1 +1,2 @@ -'vLEI' is not lower-case kabob-case \ No newline at end of file +'vLEI' is not lower-case kabob-case +Missing schema file: .*vLEI.schema.json \ No newline at end of file diff --git a/tools/check_schemas.py b/tools/check_schemas.py index d77ef19..9111b99 100644 --- a/tools/check_schemas.py +++ b/tools/check_schemas.py @@ -2,6 +2,8 @@ import sys import os import re +import json +from jsonschema import Draft202012Validator, exceptions as jsonschema_exceptions IGNORE_FILE = os.path.join(os.path.dirname(__file__), ".ignore_check_schemas") error_count = 0 @@ -35,9 +37,10 @@ def get_repo_root(): def populate_folders(): global folders repo_root = get_repo_root() + exclude = {"tools", "venv", "env"} for name in os.listdir(repo_root): path = os.path.join(repo_root, name) - if os.path.isdir(path) and not name.startswith(".") and name != "tools": + if os.path.isdir(path) and not name.startswith(".") and name not in exclude: folders.append(name) def check_folder_names(): @@ -46,6 +49,26 @@ def check_folder_names(): if not kabob_case_re.match(folder): report_error(f"Folder name '{folder}' is not lower-case kabob-case.") +def check_0_schema_basics(): + repo_root = get_repo_root() + for folder in folders: + schema_file = os.path.join(repo_root, folder, f"{folder}.schema.json") + if not os.path.isfile(schema_file): + report_error(f"Missing schema file: {schema_file}") + continue + # Check valid JSON + try: + with open(schema_file, "r") as f: + schema = json.load(f) + except Exception as e: + report_error(f"Invalid JSON in {schema_file}: {e}") + continue + # Check valid JSON Schema + try: + Draft202012Validator.check_schema(schema) + except jsonschema_exceptions.SchemaError as e: + report_error(f"Invalid JSON Schema in {schema_file}: {e}") + def main(): # Set working dir to repo root os.chdir(get_repo_root()) diff --git a/tools/requirements.txt b/tools/requirements.txt index 1bb8278..9dfab6c 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -8,4 +8,5 @@ --index-url https://pypi.org/simple/ # pypi base pip index or local pip index --editable . # install as editable +jsonschema From 30ea198fe5c39318f97cc52bc5f9cd65528ef362 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Thu, 9 Oct 2025 08:24:24 -0600 Subject: [PATCH 03/13] improve schema checking Signed-off-by: Daniel Hardman --- .github/workflows/check-schemas.yml | 2 ++ tools/check_schemas.py | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/check-schemas.yml b/.github/workflows/check-schemas.yml index bce7fe2..675bbf9 100644 --- a/.github/workflows/check-schemas.yml +++ b/.github/workflows/check-schemas.yml @@ -17,6 +17,8 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.x' + - name: Install jsonschema + run: pip install jsonschema - name: Run check_schemas.py run: | python tools/check_schemas.py diff --git a/tools/check_schemas.py b/tools/check_schemas.py index 9111b99..faac761 100644 --- a/tools/check_schemas.py +++ b/tools/check_schemas.py @@ -69,6 +69,19 @@ def check_0_schema_basics(): except jsonschema_exceptions.SchemaError as e: report_error(f"Invalid JSON Schema in {schema_file}: {e}") +def check_json_fragments(): + repo_root = get_repo_root() + for folder in folders: + folder_path = os.path.join(repo_root, folder) + for fname in os.listdir(folder_path): + if fname.endswith(".json") and not fname.endswith(".schema.json"): + fpath = os.path.join(folder_path, fname) + try: + with open(fpath, "r") as f: + json.load(f) + except Exception as e: + report_error(f"Invalid JSON in {fpath}: {e}") + def main(): # Set working dir to repo root os.chdir(get_repo_root()) From a81cb72ae07fcb6c2b6213b5d70861e4493e75bc Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Thu, 9 Oct 2025 09:06:27 -0600 Subject: [PATCH 04/13] begin forcing data types and regexes Signed-off-by: Daniel Hardman --- tools/check_schemas.py | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tools/check_schemas.py b/tools/check_schemas.py index faac761..0c509eb 100644 --- a/tools/check_schemas.py +++ b/tools/check_schemas.py @@ -9,6 +9,7 @@ error_count = 0 ignore_regexes = [] folders = [] +schemas_as_json = [] # List of (filename, jsonobj) def load_ignore_regexes(): global ignore_regexes @@ -50,6 +51,7 @@ def check_folder_names(): report_error(f"Folder name '{folder}' is not lower-case kabob-case.") def check_0_schema_basics(): + global schemas_as_json repo_root = get_repo_root() for folder in folders: schema_file = os.path.join(repo_root, folder, f"{folder}.schema.json") @@ -60,6 +62,7 @@ def check_0_schema_basics(): try: with open(schema_file, "r") as f: schema = json.load(f) + schemas_as_json.append((schema_file, schema)) except Exception as e: report_error(f"Invalid JSON in {schema_file}: {e}") continue @@ -82,6 +85,71 @@ def check_json_fragments(): except Exception as e: report_error(f"Invalid JSON in {fpath}: {e}") +def check_fields(): + pattern_re = r'^(?:[BE][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$' + dt_regex = re.compile(r'.*\Wdt$') + field_type_map = {} + a_field_type_map = {} + for filename, schema in schemas_as_json: + props = schema.get('properties', {}) + for field, spec in props.items(): + ftype = spec.get('type') + # Error if no type declared + if ftype is None: + report_error(f"In {filename}: Field '{field}' has no declared type.") + ftype = 'unknown' + field_type_map.setdefault(field, set()).add(ftype) + # Check rules for d, i, s, ri + if field in {"d", "i", "s", "ri"}: + if ftype != "string": + report_error(f"In {filename}: Field '{field}' should be type string, got {ftype}.") + pattern = spec.get('pattern') + if pattern != pattern_re: + report_error(f"In {filename}: Field '{field}' should have pattern '{pattern_re}', got '{pattern}'.") + # Check rule for dt and any field matching .*\Wdt$ + if field == "dt" or dt_regex.match(field): + if ftype != "string": + report_error(f"In {filename}: Field '{field}' should be type string, got {ftype}.") + fmt = spec.get('format') + if fmt != "date-time": + report_error(f"In {filename}: Field 'dt' should have format 'date-time', got '{fmt}'.") + # Also check fields inside /properties/a/properties + a_spec = props.get('a') + if a_spec: + a_type = a_spec.get('type') + if a_type != 'object': + report_error(f"In {filename}: Field 'a' should be type object, got {a_type}.") + # Handle oneOf or direct object + a_props = None + if a_type == 'object' and 'properties' in a_spec: + a_props = a_spec['properties'] + elif 'oneOf' in a_spec: + for option in a_spec['oneOf']: + if option.get('type') == 'object' and 'properties' in option: + a_props = option['properties'] + break + if a_props: + for field, spec in a_props.items(): + ftype = spec.get('type') + if ftype is None: + report_error(f"In {filename} (/a/properties): Field '{field}' has no declared type.") + ftype = 'unknown' + a_field_type_map.setdefault(field, set()).add(ftype) + # Check rule for dt and any field matching .*\Wdt$ + if field == "dt" or dt_regex.match(field): + if ftype != "string": + report_error(f"In {filename} (/a/properties): Field '{field}' should be type string, got {ftype}.") + fmt = spec.get('format') + if fmt != "date-time": + report_error(f"In {filename} (/a/properties): Field 'dt' should have format 'date-time', got '{fmt}'.") + # Output field name -> data types pairs for user + print("Root field name -> data types:", file=sys.stderr) + for field, types in field_type_map.items(): + print(f" {field}: {', '.join(types)}", file=sys.stderr) + print("a/properties field name -> data types:", file=sys.stderr) + for field, types in a_field_type_map.items(): + print(f" {field}: {', '.join(types)}", file=sys.stderr) + def main(): # Set working dir to repo root os.chdir(get_repo_root()) From 389a59b85291fe3fb2d01e299d384697da978dd1 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 08:02:51 -0600 Subject: [PATCH 05/13] first draft of foreign artifact Signed-off-by: Daniel Hardman --- foreign-artifact/example.json | 21 +++ foreign-artifact/foreign-artifact.schema.json | 145 ++++++++++++++++++ foreign-artifact/index.md | 98 ++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 foreign-artifact/example.json create mode 100644 foreign-artifact/foreign-artifact.schema.json create mode 100644 foreign-artifact/index.md diff --git a/foreign-artifact/example.json b/foreign-artifact/example.json new file mode 100644 index 0000000..08b2d2d --- /dev/null +++ b/foreign-artifact/example.json @@ -0,0 +1,21 @@ +{ + "v": "ACDC10JSON000000_", + "d": "SAID_PLACEHOLDER", + "i": "EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL", + "ri": "EM2YZ78SKE8eO4W1lQOJeer5xKZqLmJV7SPr3Ji5DMBZ", + "s": "SAID_PLACEHOLDER", + "a": { + "d": "SAID_PLACEHOLDER", + "dt": "2025-03-01T14:22:00.000000+00:00", + "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", + "content_type": "image/jpeg", + "content_size": 2847392, + "content_location": [ + "https://evidence.example.org/artifacts/crash-scene-photo-01.jpg", + "https://backup.example.org/artifacts/crash-scene-photo-01.jpg" + ], + "filename": "crash-scene-photo-01.SAID:EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp.jpg", + "description": "Photograph of the intersection of Main St and 5th Ave taken from the northwest corner, showing vehicle positions immediately after the collision.", + "provenance": "Captured by traffic camera AID EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL at 2025-03-01T14:19:43Z and downloaded by the insurance adjustor at 2025-03-01T14:22:00Z." + } +} \ No newline at end of file diff --git a/foreign-artifact/foreign-artifact.schema.json b/foreign-artifact/foreign-artifact.schema.json new file mode 100644 index 0000000..19c8c2e --- /dev/null +++ b/foreign-artifact/foreign-artifact.schema.json @@ -0,0 +1,145 @@ +{ + "$id": "", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Foreign Artifact", + "description": "An ACDC wrapper that gives a non-JSON artifact a cryptographic identity and attests to its provenance, enabling it to be cited as evidence in a dossier or other ACDC edge graph.", + "$comment": "Foreign artifacts have no issuee. The issuer is the party attesting to the artifact's integrity and provenance. Content integrity is anchored by a CESR-encoded hash in the content_identifier field, which SHOULD be a bytewise SAID (bSAID) or externalized SAID (xSAID) as defined in the Bytewise and Externalized SAIDs specification.", + "type": "object", + "credentialType": "foreign-artifact", + "version": "1.0.0", + "required": [ + "v", + "d", + "i", + "s", + "a" + ], + "properties": { + "v": { + "description": "Version string using ACDC conventions", + "type": "string" + }, + "d": { + "description": "SAID of this ACDC", + "type": "string" + }, + "u": { + "description": "A salty nonce, present if the issuer wishes to blind the SAID of this ACDC", + "type": "string" + }, + "i": { + "description": "AID of the issuer — the party attesting to the artifact's integrity and provenance", + "type": "string" + }, + "ri": { + "description": "Registry for issuer's credential status", + "type": "string" + }, + "s": { + "description": "SAID of this schema", + "type": "string" + }, + "a": { + "oneOf": [ + { + "description": "SAID of attributes block (compact form)", + "type": "string" + }, + { + "description": "Attributes block", + "type": "object", + "required": [ + "d", + "content_identifier", + "content_type" + ], + "properties": { + "d": { + "description": "SAID of attributes block", + "type": "string" + }, + "dt": { + "description": "Issuance datetime, ISO 8601", + "type": "string", + "format": "date-time" + }, + "content_identifier": { + "description": "A CESR-encoded cryptographic hash of the artifact's byte content. SHOULD be a bytewise SAID (bSAID) or externalized SAID (xSAID) as defined in the Bytewise and Externalized SAIDs specification, making the artifact a first-class participant in authenticated data graphs. MAY be a raw CESR hash if the artifact cannot be saidified (e.g., it was captured without an insertion point). The algorithm is self-describing via the CESR primitive code.", + "type": "string", + "examples": [ + "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp" + ] + }, + "content_type": { + "description": "MIME type of the artifact, per IANA media type registry. Use 'application/octet-stream' for arbitrary binary data with no more specific type.", + "type": "string", + "examples": [ + "image/jpeg", + "application/pdf", + "audio/mpeg", + "application/octet-stream", + "text/plain" + ] + }, + "content_size": { + "description": "Size of the artifact in bytes. Allows a recipient to anticipate transfer costs before retrieving the artifact.", + "type": "integer", + "minimum": 0 + }, + "content_location": { + "description": "One or more URIs from which the artifact may be retrieved. Multiple URIs may reflect redundant hosting, different transport protocols, or different access tiers (e.g., a public CDN and an authenticated endpoint). The presence of this field does not guarantee availability; it is informational.", + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "format": "uri" + } + } + ] + }, + "filename": { + "description": "The original filename of the artifact at the time of wrapping. For artifacts using the externalized SAID algorithm (xSAID), this field SHOULD reflect the saidified filename including the embedded SAID.", + "type": "string" + }, + "description": { + "description": "A human-readable description of the artifact's content or significance. Not intended for machine interpretation.", + "type": "string" + }, + "provenance": { + "description": "A human-readable statement of how the artifact was obtained or created — for example, 'Captured by device AID EX... at 2025-03-01T09:00:00Z', 'Downloaded from https://...', or 'Received via email from ...'. For machine-verifiable provenance, use an ACDC edge instead.", + "type": "string" + } + }, + "additionalProperties": true + } + ] + }, + "r": { + "oneOf": [ + { + "description": "Rules section SAID (compact form)", + "type": "string" + }, + { + "description": "Rules detail", + "type": "object", + "properties": { + "d": { + "description": "SAID of rules block", + "type": "string" + } + }, + "additionalProperties": true + } + ] + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/foreign-artifact/index.md b/foreign-artifact/index.md new file mode 100644 index 0000000..cd724f9 --- /dev/null +++ b/foreign-artifact/index.md @@ -0,0 +1,98 @@ +## Foreign Artifact Credentials + +### Purpose + +A Foreign Artifact credential is an ACDC wrapper that gives a non-JSON artifact +a cryptographic identity, enabling it to be cited as evidence in a dossier or +other ACDC edge graph. + +Most data in the KERI/ACDC ecosystem is natively JSON and can acquire a +self-addressing identifier (SAID) through the standard saidification algorithm +defined in the CESR specification. However, many commercially and legally +important artifact types — photographs, audio and video recordings, PDFs, +genomic data files, spreadsheets, musical scores, and arbitrary binary data — +are opaquely structured: their internal format is not JSON, and applying the +standard saidification algorithm to them is not possible. + +In addition, foreign credential types such as W3C VCs, SD-JWTs, AnonCreds, +ISO mDOC/mDL, and various flavors of signed PDF have different identifier and +issuance + verification schemes, but may need to participate in ACDC data +graphs. (The Verifiable Voice Protocol specification is an example of a +standard that explicitly contemplates such integration.) + +A Foreign Artifact credential solves this by acting as a verifiable envelope. +The artifact itself acquires a cryptographic identity either via its existing +signature (if it's a foreign credential type), or through one of the +algorithms defined in the *Bytewise and Externalized SAIDs* specification +(referred to below as the BES specification). The resulting hash — a signature, a bytewise +SAID (bSAID) or externalized SAID (xSAID) — is recorded in the `content_identifier` +field of this credential. The credential is then issued by the party attesting +to the artifact's integrity and provenance, and can be cited by a dossier edge +like any other ACDC. + +### Relationship to other specifications + +When data is a foreign credential type, + +This credential may use concepts defined in the BES specification: +*Bytewise and Externalized SAIDs* by Daniel Hardman. Implementers should read +that specification before issuing Foreign Artifact credentials. In particular: + +- The **bytewise SAID algorithm** (bSAID) is appropriate for artifacts whose + byte content can be rewritten after creation by native tooling — for example, + a JPEG whose Exif metadata can be updated, or a PDF that supports incremental + updates. The artifact receives an insertion point containing the SAID, making + the SAID intrinsic to the artifact's bytes. +- The **externalized SAID algorithm** (xSAID) is appropriate for artifacts that + cannot be safely rewritten after creation — for example, a compressed archive + or an encrypted file. The SAID is carried in the filename under a constraint + expressed inside the file content. +- When neither algorithm is practical (for example, a data stream that was + captured without an insertion point), `content_identifier` MAY hold a plain + CESR-encoded hash. In this case the artifact does not carry its own SAID, and + the integrity guarantee is weaker: the hash cannot be discovered by inspecting + the artifact itself, only by consulting the wrapper credential. + +In all cases, the CESR encoding of `content_identifier` is self-describing: the +primitive code identifies the hash algorithm, so no separate algorithm field +is needed. + +### What belongs in this credential + +A Foreign Artifact credential makes no claim about the *meaning* or *significance* +of the artifact — that interpretation is left to the dossier that cites it and +any applicable governance framework. The credential attests only to: + +- The artifact's cryptographic identity (`content_identifier`) +- Its media type (`content_type`) +- Optionally: its size, location, filename, a human-readable description, and a + provenance statement + +For machine-verifiable provenance — for example, proving that a photograph was +captured by a specific device — the appropriate mechanism is an ACDC edge in the +dossier, not the `provenance` text field in this credential. + +### Schema + +See [fa_schema.json](fa_schema.json). + +### Example + +The example ([example.json](example.json)) shows a Foreign Artifact credential +wrapping a JPEG photograph of a traffic accident scene. The `content_identifier` +field holds an xSAID, reflected also in the `filename` field. The `provenance` +field records the human-readable chain of custody from the traffic camera to +the insurance adjustor. + +### Extension + +Implementers MAY define additional fields in the `a` section beyond those +defined in this schema. Any schema that satisfies the following minimum +requirements conforms to the Foreign Artifact contract and MAY be cited as +foreign-artifact evidence in a dossier: + +1. The credential MUST be a valid ACDC with no issuee. +2. The `a` section MUST contain `content_identifier` (a CESR-encoded hash) and + `content_type` (a MIME type string). +3. The `content_identifier` SHOULD be a bSAID or xSAID as defined in the BES + specification. \ No newline at end of file From 625e110df9092fb9f5b97223ff0eb0f08b8c612c Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 08:36:19 -0600 Subject: [PATCH 06/13] improved doc Signed-off-by: Daniel Hardman --- foreign-artifact/index.md | 43 +++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/foreign-artifact/index.md b/foreign-artifact/index.md index cd724f9..55ceb3e 100644 --- a/foreign-artifact/index.md +++ b/foreign-artifact/index.md @@ -1,40 +1,35 @@ -## Foreign Artifact Credentials +## Foreign Artifact ### Purpose -A Foreign Artifact credential is an ACDC wrapper that gives a non-JSON artifact -a cryptographic identity, enabling it to be cited as evidence in a dossier or -other ACDC edge graph. - -Most data in the KERI/ACDC ecosystem is natively JSON and can acquire a -self-addressing identifier (SAID) through the standard saidification algorithm -defined in the CESR specification. However, many commercially and legally -important artifact types — photographs, audio and video recordings, PDFs, -genomic data files, spreadsheets, musical scores, and arbitrary binary data — -are opaquely structured: their internal format is not JSON, and applying the -standard saidification algorithm to them is not possible. - -In addition, foreign credential types such as W3C VCs, SD-JWTs, AnonCreds, -ISO mDOC/mDL, and various flavors of signed PDF have different identifier and -issuance + verification schemes, but may need to participate in ACDC data -graphs. (The Verifiable Voice Protocol specification is an example of a -standard that explicitly contemplates such integration.) - -A Foreign Artifact credential solves this by acting as a verifiable envelope. +A Foreign Artifact ACDC is a wrapper that gives foreign credential types +or arbitrary non-credential data the attributes that it needs to participate +fully in an ACDC data graph. + +The world is full of credential and quasi-credential data types: X509 certs, +W3C VCs, SD-JWTs, AnonCreds, ISO mDOC/mDL, a variety of tokens, and various +flavors of signed PDF. These formats have various ways to identify an artifact +and to point to parties that have a role relative to an artifact -- but we may +still want them to participate in ACDC data graphs. ([Verifiable Voice Protocol](https://datatracker.ietf.org/doc/draft-hardman-verifiable-voice-protocol/) is an example of a standard that explicitly contemplates such integration.) + +In addition, the world is full of other forms of data that should often be formally +referenced as evidence, but that are not credential-like at all: spreadsheets, documents, photographs, audio and video recordings, genomic data files, videos, musical scores, and arbitrary binary data. It is not clear how to cite such data in an ACDC. + +A Foreign Artifact credential bridges these worlds providing a verifiable envelope. The artifact itself acquires a cryptographic identity either via its existing signature (if it's a foreign credential type), or through one of the algorithms defined in the *Bytewise and Externalized SAIDs* specification -(referred to below as the BES specification). The resulting hash — a signature, a bytewise +(referred to below as the BES specification). The resulting tamper-evident identifier for the foreign artifact — a signature, a bytewise SAID (bSAID) or externalized SAID (xSAID) — is recorded in the `content_identifier` -field of this credential. The credential is then issued by the party attesting +field of this ACDC type. The ACDC is then issued by the party attesting to the artifact's integrity and provenance, and can be cited by a dossier edge like any other ACDC. ### Relationship to other specifications -When data is a foreign credential type, +When data *inherently* carries a digitally signed (typically, a foreign credential type), the signature of the data is its identifier, and whatever specification describes its signing algorithm becomes normative for how that signature is verified. -This credential may use concepts defined in the BES specification: +When data isn't inherently signed, it should be referenced as described in BES specification: *Bytewise and Externalized SAIDs* by Daniel Hardman. Implementers should read that specification before issuing Foreign Artifact credentials. In particular: From 22ff8517ddba6454ddb57137dc6b35d593fce8b2 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:17:33 -0600 Subject: [PATCH 07/13] improve schema Signed-off-by: Daniel Hardman --- {foreign-artifact => faa}/example.json | 11 ++- faa/example.json.new.bak | 24 +++++ .../faa.schema.json | 44 ++++++--- faa/index.md | 44 +++++++++ faa/x.json.new.bak | 17 ++++ foreign-artifact/index.md | 93 ------------------- 6 files changed, 124 insertions(+), 109 deletions(-) rename {foreign-artifact => faa}/example.json (76%) create mode 100644 faa/example.json.new.bak rename foreign-artifact/foreign-artifact.schema.json => faa/faa.schema.json (60%) create mode 100644 faa/index.md create mode 100644 faa/x.json.new.bak delete mode 100644 foreign-artifact/index.md diff --git a/foreign-artifact/example.json b/faa/example.json similarity index 76% rename from foreign-artifact/example.json rename to faa/example.json index 08b2d2d..c4492e0 100644 --- a/foreign-artifact/example.json +++ b/faa/example.json @@ -1,13 +1,16 @@ { - "v": "ACDC10JSON000000_", - "d": "SAID_PLACEHOLDER", + "v": "ACDC10JSON00044b_", + "d": "ED_jB_CgaoUrmd7d9Ln3gcq0ArsvOCs0EkGJpH_ubnhU", "i": "EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL", "ri": "EM2YZ78SKE8eO4W1lQOJeer5xKZqLmJV7SPr3Ji5DMBZ", - "s": "SAID_PLACEHOLDER", + "s": "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8", "a": { - "d": "SAID_PLACEHOLDER", + "d": "EFM0s2YlM32LAh0h77tvkz3OR6AaYmHcVchm9oVNkvbI", + "u": "0ADY50r_UJ3OaDC9ww7UcwXM", "dt": "2025-03-01T14:22:00.000000+00:00", "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", + "art_posture": "witness", + "rev_latency": 0, "content_type": "image/jpeg", "content_size": 2847392, "content_location": [ diff --git a/faa/example.json.new.bak b/faa/example.json.new.bak new file mode 100644 index 0000000..0f9830d --- /dev/null +++ b/faa/example.json.new.bak @@ -0,0 +1,24 @@ +{ + "v": "ACDC10JSON000000_", + "d": "", + "i": "EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL", + "ri": "EM2YZ78SKE8eO4W1lQOJeer5xKZqLmJV7SPr3Ji5DMBZ", + "s": "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8", + "a": { + "d": "EFM0s2YlM32LAh0h77tvkz3OR6AaYmHcVchm9oVNkvbI", + "u": "0ADY50r_UJ3OaDC9ww7UcwXM", + "dt": "2025-03-01T14:22:00.000000+00:00", + "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", + "art_posture": "witness", + "rev_latency": 0, + "content_type": "image/jpeg", + "content_size": 2847392, + "content_location": [ + "https://evidence.example.org/artifacts/crash-scene-photo-01.jpg", + "https://backup.example.org/artifacts/crash-scene-photo-01.jpg" + ], + "filename": "crash-scene-photo-01.SAID:EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp.jpg", + "description": "Photograph of the intersection of Main St and 5th Ave taken from the northwest corner, showing vehicle positions immediately after the collision.", + "provenance": "Captured by traffic camera AID EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL at 2025-03-01T14:19:43Z and downloaded by the insurance adjustor at 2025-03-01T14:22:00Z." + } +} \ No newline at end of file diff --git a/foreign-artifact/foreign-artifact.schema.json b/faa/faa.schema.json similarity index 60% rename from foreign-artifact/foreign-artifact.schema.json rename to faa/faa.schema.json index 19c8c2e..67776f2 100644 --- a/foreign-artifact/foreign-artifact.schema.json +++ b/faa/faa.schema.json @@ -1,11 +1,10 @@ { - "$id": "", + "$id": "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8", "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Foreign Artifact", - "description": "An ACDC wrapper that gives a non-JSON artifact a cryptographic identity and attests to its provenance, enabling it to be cited as evidence in a dossier or other ACDC edge graph.", - "$comment": "Foreign artifacts have no issuee. The issuer is the party attesting to the artifact's integrity and provenance. Content integrity is anchored by a CESR-encoded hash in the content_identifier field, which SHOULD be a bytewise SAID (bSAID) or externalized SAID (xSAID) as defined in the Bytewise and Externalized SAIDs specification.", + "title": "Foreign Artifact Affidavit", + "description": "An ACDC wrapper that gives arbitrary binary data a tamper-evident, cryptographic identity, and that documents key attributes. This allows it to be cited as evidence in a verifiable data graph.", "type": "object", - "credentialType": "foreign-artifact", + "credentialType": "foreign-artifact-affidavit", "version": "1.0.0", "required": [ "v", @@ -24,11 +23,11 @@ "type": "string" }, "u": { - "description": "A salty nonce, present if the issuer wishes to blind the SAID of this ACDC", + "description": "A salty nonce, present if the issuer wishes to blind the attributes of this ACDC", "type": "string" }, "i": { - "description": "AID of the issuer — the party attesting to the artifact's integrity and provenance", + "description": "AID of the issuer \u2014 the party attesting to the artifact's integrity and provenance", "type": "string" }, "ri": { @@ -47,32 +46,53 @@ }, { "description": "Attributes block", + "$id": "EHH-xs758EzJpUC1n4Et18n5nGc5vmM7w2KaqojwxJ40", "type": "object", "required": [ "d", "content_identifier", - "content_type" + "ref_posture" ], "properties": { "d": { "description": "SAID of attributes block", "type": "string" }, + "u": { + "description": "A salty nonce", + "type": "string" + }, "dt": { "description": "Issuance datetime, ISO 8601", "type": "string", "format": "date-time" }, "content_identifier": { - "description": "A CESR-encoded cryptographic hash of the artifact's byte content. SHOULD be a bytewise SAID (bSAID) or externalized SAID (xSAID) as defined in the Bytewise and Externalized SAIDs specification, making the artifact a first-class participant in authenticated data graphs. MAY be a raw CESR hash if the artifact cannot be saidified (e.g., it was captured without an insertion point). The algorithm is self-describing via the CESR primitive code.", + "description": "A CESR-encoded cryptographic hash of the artifact. SHOULD be a SAID; see https://doi.org/10.2139/ssrn.6128466 for saidification options. MAY be a raw CESR hash if saidification is impractical.", "type": "string", + "pattern": "^(?:[EFGHI][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$", "examples": [ "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp" ] }, + "art_posture": { + "description": "Posture of the issuer with respect to the artifact. Must be one of: 'record' (record the artifact's identifer without necessarily seeing the artifact), 'witness' (witness the artifact and compute content_digest from it), or 'verify' (verify the artifact according to its native rules).", + "type": "string", + "enum": [ + "record", + "witness", + "verify" + ] + }, + "rev_latency": { + "description": "Assertion that the issuer makes about how quickly it will revoke this ACDC if its corresponding artifact is revoked. A non-negative integer measuring seconds of elapsed time between the artifact revocation and its subsequent ACDC revocation. The most common value is 0, meaning the artifact has no revocation semantics, and/or the issuer takes no responsibility for keeping the ACDC's revocation aligned with the artifact's. When the artifact is a foreign credential type, and the FAA issuer acts as a bridge, this field allows an indirect revocation test of the foreign value, if the issuer is trusted.", + "type": "integer", + "minimum": 0 + }, "content_type": { - "description": "MIME type of the artifact, per IANA media type registry. Use 'application/octet-stream' for arbitrary binary data with no more specific type.", + "description": "MIME type of the artifact, per IANA media type registry. Although 'application/octet-stream' is valid, overly generic types are discouraged because the limit low-context data processing.", "type": "string", + "pattern": "^[a-z]+/[a-z0-9][a-z0-9._+-]*$", "examples": [ "image/jpeg", "application/pdf", @@ -87,7 +107,7 @@ "minimum": 0 }, "content_location": { - "description": "One or more URIs from which the artifact may be retrieved. Multiple URIs may reflect redundant hosting, different transport protocols, or different access tiers (e.g., a public CDN and an authenticated endpoint). The presence of this field does not guarantee availability; it is informational.", + "description": "One or more URIs from which the artifact may be retrieved. Multiple URIs may reflect redundant hosting, different transport protocols, or different access tiers (e.g., a public CDN and an authenticated endpoint). The presence of this field does not guarantee availability; it is informational. However, use of data:// URIs is supported and allows direct embedding of the content.", "oneOf": [ { "type": "string", @@ -113,7 +133,7 @@ "type": "string" }, "provenance": { - "description": "A human-readable statement of how the artifact was obtained or created — for example, 'Captured by device AID EX... at 2025-03-01T09:00:00Z', 'Downloaded from https://...', or 'Received via email from ...'. For machine-verifiable provenance, use an ACDC edge instead.", + "description": "A human-readable statement of how the artifact was obtained or created \u2014 for example, 'Captured by device AID EX... at 2025-03-01T09:00:00Z', 'Downloaded from https://...', or 'Received via email from ...'. For machine-verifiable provenance, use an ACDC edge instead.", "type": "string" } }, diff --git a/faa/index.md b/faa/index.md new file mode 100644 index 0000000..9442a39 --- /dev/null +++ b/faa/index.md @@ -0,0 +1,44 @@ +## Foreign Artifact Affidavit (FAA) + +### Purpose + +A Foreign Artifact Affidavit (FAA, pronounced `/fa/` like the musical syllable) is a wrapper that gives non-ACDC data the attributes that are needed to participate fully in an ACDC data graph. It is a simple but powerful interoperability mechanism. + +Modern life is full of credential and credential-like data types: X509 certs, W3C VCs, SD-JWTs, AnonCreds, ISO mDOC/mDL, remote attestations from a secure enclave, a variety of tokens, and various flavors of signed PDF are digital examples; birth certificates, passports, and citizen ID cards are physical examples. Instances of these artifacts have various ways to identify themselves and to point to parties that have a role relative to them -- but we may still want them to participate in standard ACDC data graphs. ([Verifiable Voice Protocol](https://datatracker.ietf.org/doc/draft-hardman-verifiable-voice-protocol/) is an example of an evidence-dependent industry mechanism that explicitly contemplates such integration.) + +In addition, we know of other, arbitrary forms of data that often need to be formally referenced as evidence, but that are not credential-like at all: spreadsheets, documents, photographs, audio and video recordings, sensor readings, fingerprints, videos, musical scores, G-codes, blood-glucose measurements, courtroom transcriptions. And non-digital embodiments are also important here: soil samples, biopsies, genomes, analog recordings. It is not clear how to cite arbitrary data in an ACDC. + +A FAA places non-ACDC data in an efficient, tamper-evident envelope with predictable metadata. This lets the ACDC ecosystem cite it, reason about it, and verify some of its properties in standard ways. + +### Physical to digital + +Specifying how to digitize data is out of scope, but we make the simplifying assumption that digitization will occur in some way that's meaningful to ecosystem participants. X-rays and ultrasounds turn into DICOM images, genomes turn into FASTQ or VCF, dental impressions turn into STL, analog signals from a radiotelescope turn into FITS, the bumper of a crashed car turns into photos at the accident scene, and so forth. + +### Arbitrary digital to tamper-evident digital + +Once a reference version of the data is available digitally, we need a way to refer to it. This reference must be tamper-evident (at least to the fidelity of the sample resolution, if quantized). + +ACDCs use hashes of the full content of the data for this purpose. If data lends itself to standard *saidification*, the SAID of the data SHOULD identify it. Otherwise, the [bytewise or externalized SAID algorithms](https://doi.org/10.2139/ssrn.6128466) SHOULD be applied to the data, or the data MAY simply be hashed. In all cases, the resulting digest is encoded as CESR, and in this form becomes the tamper-evident, self-describing way to reference the digital data it derives from. This CESR-encoded digest is stored in the `content_digest` field in the FAA schema. The data identified by this digest is called the *artifact* of the FAA. + +### What belongs in this credential + +In and of itself, a FAA makes no claim about the *meaning* or *significance* of its artifact. That interpretation is left to the ACDC (e.g., a [dossier](https://trustoverip.github.io/kswg-dossier-specification) that cites it and to any governance framework that provides context. The FAA attests to: + +- The artifact's cryptographic identity (`content_digest`) +- Optional metadata about the artifact: content type, size, location, filename, a human-readable description, a + description of provenance, issuance date +- The issuer's posture with respect to the artifact (`art_posture`): did the issuer merely record `content_digest` at the request of a third party, or witness the artifact directly and compute `content_digest` from it, or actually verify the integrity of the artifact at a particular moment, according to that artifact's native rules? +- The issuer's assertion with respect to revocation of the artifact (`rev_latency`): if the artifact is revoked, does the issuer of the FAA attempt a corresponding revocation of this ACDC -- and if so, how quickly? (Normally, the issuer of an FAA makes no attempt to react to revocation events of its corresponding artifact, either because the artfact has no revocation semantics, or because tracking revocation is a burden on an issuer that's just trying to create a lightweight affidavit. However, if the FAA is issued by an entity that's actively trying to bridge between another credential ecosystem and ACDCs, a non-zero value here allows verifiers to treat the FAA as a proxy for the foreign credential, assuming they trust the FAA issuer.) + +### Schema + +See [foreign-artifact.schema.json](foreign-artifact.schema.json). + +### Example + +The example ([example.json](example.json)) shows a FAA wrapping a JPEG photograph of a traffic accident scene. The `content_digest` field holds an xSAID, reflected also in the `filename` field. The `provenance` field records the human-readable chain of custody from the traffic camera to the insurance adjustor. + +### Extension + +Implementers MAY define additional fields in the `a` section beyond those +defined in this schema. \ No newline at end of file diff --git a/faa/x.json.new.bak b/faa/x.json.new.bak new file mode 100644 index 0000000..e7236f2 --- /dev/null +++ b/faa/x.json.new.bak @@ -0,0 +1,17 @@ +{ + "d": "EBYj1VT43Li-gmTxk8XhpHSpzmyQxCpKcihw_GSp7b5Q", + "u": "0ADY50r_UJ3OaDC9ww7UcwXM", + "dt": "2025-03-01T14:22:00.000000+00:00", + "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", + "art_posture": "witness", + "rev_latency": 0, + "content_type": "image/jpeg", + "content_size": 2847392, + "content_location": [ + "https://evidence.example.org/artifacts/crash-scene-photo-01.jpg", + "https://backup.example.org/artifacts/crash-scene-photo-01.jpg" + ], + "filename": "crash-scene-photo-01.SAID:EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp.jpg", + "description": "Photograph of the intersection of Main St and 5th Ave taken from the northwest corner, showing vehicle positions immediately after the collision.", + "provenance": "Captured by traffic camera AID EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL at 2025-03-01T14:19:43Z and downloaded by the insurance adjustor at 2025-03-01T14:22:00Z." +} \ No newline at end of file diff --git a/foreign-artifact/index.md b/foreign-artifact/index.md deleted file mode 100644 index 55ceb3e..0000000 --- a/foreign-artifact/index.md +++ /dev/null @@ -1,93 +0,0 @@ -## Foreign Artifact - -### Purpose - -A Foreign Artifact ACDC is a wrapper that gives foreign credential types -or arbitrary non-credential data the attributes that it needs to participate -fully in an ACDC data graph. - -The world is full of credential and quasi-credential data types: X509 certs, -W3C VCs, SD-JWTs, AnonCreds, ISO mDOC/mDL, a variety of tokens, and various -flavors of signed PDF. These formats have various ways to identify an artifact -and to point to parties that have a role relative to an artifact -- but we may -still want them to participate in ACDC data graphs. ([Verifiable Voice Protocol](https://datatracker.ietf.org/doc/draft-hardman-verifiable-voice-protocol/) is an example of a standard that explicitly contemplates such integration.) - -In addition, the world is full of other forms of data that should often be formally -referenced as evidence, but that are not credential-like at all: spreadsheets, documents, photographs, audio and video recordings, genomic data files, videos, musical scores, and arbitrary binary data. It is not clear how to cite such data in an ACDC. - -A Foreign Artifact credential bridges these worlds providing a verifiable envelope. -The artifact itself acquires a cryptographic identity either via its existing -signature (if it's a foreign credential type), or through one of the -algorithms defined in the *Bytewise and Externalized SAIDs* specification -(referred to below as the BES specification). The resulting tamper-evident identifier for the foreign artifact — a signature, a bytewise -SAID (bSAID) or externalized SAID (xSAID) — is recorded in the `content_identifier` -field of this ACDC type. The ACDC is then issued by the party attesting -to the artifact's integrity and provenance, and can be cited by a dossier edge -like any other ACDC. - -### Relationship to other specifications - -When data *inherently* carries a digitally signed (typically, a foreign credential type), the signature of the data is its identifier, and whatever specification describes its signing algorithm becomes normative for how that signature is verified. - -When data isn't inherently signed, it should be referenced as described in BES specification: -*Bytewise and Externalized SAIDs* by Daniel Hardman. Implementers should read -that specification before issuing Foreign Artifact credentials. In particular: - -- The **bytewise SAID algorithm** (bSAID) is appropriate for artifacts whose - byte content can be rewritten after creation by native tooling — for example, - a JPEG whose Exif metadata can be updated, or a PDF that supports incremental - updates. The artifact receives an insertion point containing the SAID, making - the SAID intrinsic to the artifact's bytes. -- The **externalized SAID algorithm** (xSAID) is appropriate for artifacts that - cannot be safely rewritten after creation — for example, a compressed archive - or an encrypted file. The SAID is carried in the filename under a constraint - expressed inside the file content. -- When neither algorithm is practical (for example, a data stream that was - captured without an insertion point), `content_identifier` MAY hold a plain - CESR-encoded hash. In this case the artifact does not carry its own SAID, and - the integrity guarantee is weaker: the hash cannot be discovered by inspecting - the artifact itself, only by consulting the wrapper credential. - -In all cases, the CESR encoding of `content_identifier` is self-describing: the -primitive code identifies the hash algorithm, so no separate algorithm field -is needed. - -### What belongs in this credential - -A Foreign Artifact credential makes no claim about the *meaning* or *significance* -of the artifact — that interpretation is left to the dossier that cites it and -any applicable governance framework. The credential attests only to: - -- The artifact's cryptographic identity (`content_identifier`) -- Its media type (`content_type`) -- Optionally: its size, location, filename, a human-readable description, and a - provenance statement - -For machine-verifiable provenance — for example, proving that a photograph was -captured by a specific device — the appropriate mechanism is an ACDC edge in the -dossier, not the `provenance` text field in this credential. - -### Schema - -See [fa_schema.json](fa_schema.json). - -### Example - -The example ([example.json](example.json)) shows a Foreign Artifact credential -wrapping a JPEG photograph of a traffic accident scene. The `content_identifier` -field holds an xSAID, reflected also in the `filename` field. The `provenance` -field records the human-readable chain of custody from the traffic camera to -the insurance adjustor. - -### Extension - -Implementers MAY define additional fields in the `a` section beyond those -defined in this schema. Any schema that satisfies the following minimum -requirements conforms to the Foreign Artifact contract and MAY be cited as -foreign-artifact evidence in a dossier: - -1. The credential MUST be a valid ACDC with no issuee. -2. The `a` section MUST contain `content_identifier` (a CESR-encoded hash) and - `content_type` (a MIME type string). -3. The `content_identifier` SHOULD be a bSAID or xSAID as defined in the BES - specification. \ No newline at end of file From 2ffa7669aa045f98328f8864bb6084f86bf15f51 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:18:07 -0600 Subject: [PATCH 08/13] remove irrelevant backup file Signed-off-by: Daniel Hardman --- faa/x.json.new.bak | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 faa/x.json.new.bak diff --git a/faa/x.json.new.bak b/faa/x.json.new.bak deleted file mode 100644 index e7236f2..0000000 --- a/faa/x.json.new.bak +++ /dev/null @@ -1,17 +0,0 @@ -{ - "d": "EBYj1VT43Li-gmTxk8XhpHSpzmyQxCpKcihw_GSp7b5Q", - "u": "0ADY50r_UJ3OaDC9ww7UcwXM", - "dt": "2025-03-01T14:22:00.000000+00:00", - "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", - "art_posture": "witness", - "rev_latency": 0, - "content_type": "image/jpeg", - "content_size": 2847392, - "content_location": [ - "https://evidence.example.org/artifacts/crash-scene-photo-01.jpg", - "https://backup.example.org/artifacts/crash-scene-photo-01.jpg" - ], - "filename": "crash-scene-photo-01.SAID:EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp.jpg", - "description": "Photograph of the intersection of Main St and 5th Ave taken from the northwest corner, showing vehicle positions immediately after the collision.", - "provenance": "Captured by traffic camera AID EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL at 2025-03-01T14:19:43Z and downloaded by the insurance adjustor at 2025-03-01T14:22:00Z." -} \ No newline at end of file From 46c9ba774318796a78ba9a40b43d7e2b7531b128 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:26:03 -0600 Subject: [PATCH 09/13] updated registry Signed-off-by: Daniel Hardman --- org-vet/org-vet.schema.json | 4 ++-- registry.json | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/org-vet/org-vet.schema.json b/org-vet/org-vet.schema.json index 8e20f32..19bbcd1 100644 --- a/org-vet/org-vet.schema.json +++ b/org-vet/org-vet.schema.json @@ -1,5 +1,5 @@ { - "$id": "EK0QuXKzVQ0yXaZYXRp_nuwDoxZpuH03_AP6qlZ-MV5m", + "$id": "EJrcLKzq4d1PFtlnHLb9tl4zGwPAjO6v0dec4CiJMZk6", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Org Vet", "description": "Authenticate an org with an explicit level of assurance.", @@ -37,7 +37,7 @@ "type": "string" }, "a": { - "$id": "EOt6UyUDfF4Ocw1UHAI8rhEQIHOTswqNS2sT2zaXreI6", + "$id": "ELtdYgSHM9VElQtjd4P19IB24HH8PQZGbEctOjmP69QE", "description": "Attributes block", "type": "object", "required": [ diff --git a/registry.json b/registry.json index 9d2455c..85b4522 100644 --- a/registry.json +++ b/registry.json @@ -1,24 +1,27 @@ { + "EPy_7LE3tVdl8qEKN5i4L8eAgIM-1I51-DNiewmcq-fe": "tcr-vetting/tcr-vetting.schema.json", "EK3YbEFp3zUuHPsGrLRzwr6zWBXcOFJNTTBqezXd_2yf": "a2p-campaign/a2p-campaign.schema.json", - "EBxJHMk6MOEUogB6A1rP5x9te7DscPfxFfUGJCkq1Lq5": "award/award.schema.json", - "EBCnd7qk82wLBOgFukdmsdkksAuPpmzt5-eg9YKWWP3j": "ai-user-coca/ai-user-coca.schema.json", + "EFv3_L64_xNhOGQkaAHQTI-lzQYDvlaHcuZbuOTuDBXj": "vvp-dossier/vvp-dossier.schema.json", "EFvnoHDY7I-kaBBeKlbDbkjG4BaI0nKLGadxBdjMGgSQ": "tn-alloc/tn-alloc.schema.json", + "EJxFPpyDRV-W6O2Vtjdy2K90ltWmQK8l1jePw5YOo_Ft": "attestation/attestation.schema.json", "EKrv2S0OVc8SeKCzIAOSE-y4j5ybLOOgB69y12Lzxh6Y": "face-to-face/face-to-face.schema.json", - "EPhWFgeOy8g7yRy-Xtyvbdieqvl_3YVXNHMgTEZuJOWh": "ai-coder/ai-coder.schema.json", - "EL7irIKYJL9Io0hhKSGWI4OznhwC7qgJG5Qf4aEs6j0o": "gcd/gcd.schema.json", - "EFv3_L64_xNhOGQkaAHQTI-lzQYDvlaHcuZbuOTuDBXj": "vvp-dossier/vvp-dossier.schema.json", + "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8": "faa/faa.schema.json", "EFvHYHX0cUx9sdjxZOr9fpPcQKdzRNFH42D8R29p7lAH": "bindkey/bindkey.schema.json", - "EJxFPpyDRV-W6O2Vtjdy2K90ltWmQK8l1jePw5YOo_Ft": "attestation/attestation.schema.json", - "EPy_7LE3tVdl8qEKN5i4L8eAgIM-1I51-DNiewmcq-fe": "tcr-vetting/tcr-vetting.schema.json", + "EPhWFgeOy8g7yRy-Xtyvbdieqvl_3YVXNHMgTEZuJOWh": "ai-coder/ai-coder.schema.json", "EG68irpfVX667KCLwG85Cn1Mp3sCe38ftARyQJrxP2kF": "proof-of-control/proof-of-control.schema.json", - "EKR1nFV0JvRhc4xkhzlZJfpN_taaspipcZWWSLj37Fdk": "aegis-std-vetting/aegis-std-vetting.schema.json", + "EBxJHMk6MOEUogB6A1rP5x9te7DscPfxFfUGJCkq1Lq5": "award/award.schema.json", + "EDH3Q0MW6oCcwyYw2MN39n1YfPs37o1QEv86kB-fBzmh": "citation/citation.schema.json", "EGEebb1pVRcZ6OXHlYitl5DNh-LDrMWPwRtstiKiDhRy": "tn/tn.schema.json", - "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy": "vLEI/acdc/legal-entity-official-organizational-role-vLEI-credential.json", + "EBCnd7qk82wLBOgFukdmsdkksAuPpmzt5-eg9YKWWP3j": "ai-user-coca/ai-user-coca.schema.json", + "EJrcLKzq4d1PFtlnHLb9tl4zGwPAjO6v0dec4CiJMZk6": "org-vet/org-vet.schema.json", + "EL7irIKYJL9Io0hhKSGWI4OznhwC7qgJG5Qf4aEs6j0o": "gcd/gcd.schema.json", + "EKR1nFV0JvRhc4xkhzlZJfpN_taaspipcZWWSLj37Fdk": "aegis-std-vetting/aegis-std-vetting.schema.json", "EMhvwOlyEJ9kN4PrwCpr9Jsv7TxPhiYveZ0oP3lJzdEi": "vLEI/acdc/verifiable-ixbrl-report-attestation.json", - "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E": "vLEI/acdc/oor-authorization-vlei-credential.json", + "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy": "vLEI/acdc/legal-entity-official-organizational-role-vLEI-credential.json", "EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw": "vLEI/acdc/legal-entity-engagement-context-role-vLEI-credential.json", "ECYorXkheU7YsXZkYLGtvBOxEZ6alS5H5FJRn0tgDXV0": "vLEI/acdc/verifiable-ixbrl-report-d6-attestation.json", "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY": "vLEI/acdc/legal-entity-vLEI-credential.json", + "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao": "vLEI/acdc/qualified-vLEI-issuer-vLEI-credential.json", "EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g": "vLEI/acdc/ecr-authorization-vlei-credential.json", - "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao": "vLEI/acdc/qualified-vLEI-issuer-vLEI-credential.json" + "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E": "vLEI/acdc/oor-authorization-vlei-credential.json" } \ No newline at end of file From 3422f5d3839ec9a489aab21ba7e1b1705d6ffca4 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:35:22 -0600 Subject: [PATCH 10/13] rename content_digest to art_digest Signed-off-by: Daniel Hardman --- faa/example.json | 2 +- faa/faa.schema.json | 6 +++--- faa/index.md | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/faa/example.json b/faa/example.json index c4492e0..bce80fe 100644 --- a/faa/example.json +++ b/faa/example.json @@ -8,7 +8,7 @@ "d": "EFM0s2YlM32LAh0h77tvkz3OR6AaYmHcVchm9oVNkvbI", "u": "0ADY50r_UJ3OaDC9ww7UcwXM", "dt": "2025-03-01T14:22:00.000000+00:00", - "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", + "art_digest": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", "art_posture": "witness", "rev_latency": 0, "content_type": "image/jpeg", diff --git a/faa/faa.schema.json b/faa/faa.schema.json index 67776f2..5c6c03a 100644 --- a/faa/faa.schema.json +++ b/faa/faa.schema.json @@ -50,8 +50,8 @@ "type": "object", "required": [ "d", - "content_identifier", - "ref_posture" + "art_digest", + "art_posture" ], "properties": { "d": { @@ -76,7 +76,7 @@ ] }, "art_posture": { - "description": "Posture of the issuer with respect to the artifact. Must be one of: 'record' (record the artifact's identifer without necessarily seeing the artifact), 'witness' (witness the artifact and compute content_digest from it), or 'verify' (verify the artifact according to its native rules).", + "description": "Posture of the issuer with respect to the artifact. Must be one of: 'record' (record the artifact's identifer without necessarily seeing the artifact), 'witness' (witness the artifact and compute art_digest from it), or 'verify' (verify the artifact according to its native rules).", "type": "string", "enum": [ "record", diff --git a/faa/index.md b/faa/index.md index 9442a39..d6fbbb5 100644 --- a/faa/index.md +++ b/faa/index.md @@ -18,16 +18,16 @@ Specifying how to digitize data is out of scope, but we make the simplifying ass Once a reference version of the data is available digitally, we need a way to refer to it. This reference must be tamper-evident (at least to the fidelity of the sample resolution, if quantized). -ACDCs use hashes of the full content of the data for this purpose. If data lends itself to standard *saidification*, the SAID of the data SHOULD identify it. Otherwise, the [bytewise or externalized SAID algorithms](https://doi.org/10.2139/ssrn.6128466) SHOULD be applied to the data, or the data MAY simply be hashed. In all cases, the resulting digest is encoded as CESR, and in this form becomes the tamper-evident, self-describing way to reference the digital data it derives from. This CESR-encoded digest is stored in the `content_digest` field in the FAA schema. The data identified by this digest is called the *artifact* of the FAA. +ACDCs use hashes of the full content of the data for this purpose. If data lends itself to standard *saidification*, the SAID of the data SHOULD identify it. Otherwise, the [bytewise or externalized SAID algorithms](https://doi.org/10.2139/ssrn.6128466) SHOULD be applied to the data, or the data MAY simply be hashed. In all cases, the resulting digest is encoded as CESR, and in this form becomes the tamper-evident, self-describing way to reference the digital data it derives from. This CESR-encoded digest is stored in the `art_digest` field in the FAA schema. The data identified by this digest is called the *artifact* of the FAA. ### What belongs in this credential In and of itself, a FAA makes no claim about the *meaning* or *significance* of its artifact. That interpretation is left to the ACDC (e.g., a [dossier](https://trustoverip.github.io/kswg-dossier-specification) that cites it and to any governance framework that provides context. The FAA attests to: -- The artifact's cryptographic identity (`content_digest`) +- The artifact's cryptographic identity (`art_digest`) - Optional metadata about the artifact: content type, size, location, filename, a human-readable description, a description of provenance, issuance date -- The issuer's posture with respect to the artifact (`art_posture`): did the issuer merely record `content_digest` at the request of a third party, or witness the artifact directly and compute `content_digest` from it, or actually verify the integrity of the artifact at a particular moment, according to that artifact's native rules? +- The issuer's posture with respect to the artifact (`art_posture`): did the issuer merely record `art_digest` at the request of a third party, or witness the artifact directly and compute `art_digest` from it, or actually verify the integrity of the artifact at a particular moment, according to that artifact's native rules? - The issuer's assertion with respect to revocation of the artifact (`rev_latency`): if the artifact is revoked, does the issuer of the FAA attempt a corresponding revocation of this ACDC -- and if so, how quickly? (Normally, the issuer of an FAA makes no attempt to react to revocation events of its corresponding artifact, either because the artfact has no revocation semantics, or because tracking revocation is a burden on an issuer that's just trying to create a lightweight affidavit. However, if the FAA is issued by an entity that's actively trying to bridge between another credential ecosystem and ACDCs, a non-zero value here allows verifiers to treat the FAA as a proxy for the foreign credential, assuming they trust the FAA issuer.) ### Schema @@ -36,7 +36,7 @@ See [foreign-artifact.schema.json](foreign-artifact.schema.json). ### Example -The example ([example.json](example.json)) shows a FAA wrapping a JPEG photograph of a traffic accident scene. The `content_digest` field holds an xSAID, reflected also in the `filename` field. The `provenance` field records the human-readable chain of custody from the traffic camera to the insurance adjustor. +The example ([example.json](example.json)) shows a FAA wrapping a JPEG photograph of a traffic accident scene. The `art_digest` field holds an xSAID, reflected also in the `filename` field. The `provenance` field records the human-readable chain of custody from the traffic camera to the insurance adjustor. ### Extension From 09686ad08067e52786e3f4e1a5b10d7505497b49 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:38:59 -0600 Subject: [PATCH 11/13] fix minor bugs Signed-off-by: Daniel Hardman --- faa/index.md | 2 +- tools/check_schemas.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/faa/index.md b/faa/index.md index d6fbbb5..a3e226a 100644 --- a/faa/index.md +++ b/faa/index.md @@ -32,7 +32,7 @@ In and of itself, a FAA makes no claim about the *meaning* or *significance* of ### Schema -See [foreign-artifact.schema.json](foreign-artifact.schema.json). +See [faa.schema.json](faa.schema.json). ### Example diff --git a/tools/check_schemas.py b/tools/check_schemas.py index 0c509eb..c9dfd21 100644 --- a/tools/check_schemas.py +++ b/tools/check_schemas.py @@ -117,7 +117,7 @@ def check_fields(): a_spec = props.get('a') if a_spec: a_type = a_spec.get('type') - if a_type != 'object': + if a_type != 'object' and 'oneOf' not in a_spec: report_error(f"In {filename}: Field 'a' should be type object, got {a_type}.") # Handle oneOf or direct object a_props = None From e4114579b254160629adc06400e9be3058437407 Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:49:04 -0600 Subject: [PATCH 12/13] sort registry by path Signed-off-by: Daniel Hardman --- faa/faa.schema.json | 18 +++++++++++------- registry.json | 36 +++++++++++++++++------------------- tools/cli/register_all.py | 3 ++- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/faa/faa.schema.json b/faa/faa.schema.json index 5c6c03a..520a9a7 100644 --- a/faa/faa.schema.json +++ b/faa/faa.schema.json @@ -1,5 +1,5 @@ { - "$id": "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8", + "$id": "ELuPsd1mylvD9iY_gH5hEJzoUd8OZWtU6J2svJwIq-TJ", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Foreign Artifact Affidavit", "description": "An ACDC wrapper that gives arbitrary binary data a tamper-evident, cryptographic identity, and that documents key attributes. This allows it to be cited as evidence in a verifiable data graph.", @@ -20,23 +20,26 @@ }, "d": { "description": "SAID of this ACDC", - "type": "string" + "type": "string", + "pattern": "^(?:[BE][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$" }, "u": { "description": "A salty nonce, present if the issuer wishes to blind the attributes of this ACDC", "type": "string" }, "i": { - "description": "AID of the issuer \u2014 the party attesting to the artifact's integrity and provenance", + "description": "AID of the issuer -- the party attesting to the artifact's integrity and provenance", "type": "string" }, "ri": { "description": "Registry for issuer's credential status", - "type": "string" + "type": "string", + "pattern": "^(?:[BE][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$" }, "s": { "description": "SAID of this schema", - "type": "string" + "type": "string", + "pattern": "^(?:[BE][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$" }, "a": { "oneOf": [ @@ -46,7 +49,7 @@ }, { "description": "Attributes block", - "$id": "EHH-xs758EzJpUC1n4Et18n5nGc5vmM7w2KaqojwxJ40", + "$id": "EHhP6un6a3BqmyTJbCJ4TBz4cctdDgKc9TAi7-cEUrDJ", "type": "object", "required": [ "d", @@ -56,7 +59,8 @@ "properties": { "d": { "description": "SAID of attributes block", - "type": "string" + "type": "string", + "pattern": "^(?:[BE][A-Za-z0-9_-]{43}|0[DEFG][A-Za-z0-9_-]{86})$" }, "u": { "description": "A salty nonce", diff --git a/registry.json b/registry.json index 3b17f57..14f952d 100644 --- a/registry.json +++ b/registry.json @@ -1,32 +1,30 @@ { "EK3YbEFp3zUuHPsGrLRzwr6zWBXcOFJNTTBqezXd_2yf": "a2p-campaign/a2p-campaign.schema.json", - "EFvnoHDY7I-kaBBeKlbDbkjG4BaI0nKLGadxBdjMGgSQ": "tn-alloc/tn-alloc.schema.json", + "EKR1nFV0JvRhc4xkhzlZJfpN_taaspipcZWWSLj37Fdk": "aegis-std-vetting/aegis-std-vetting.schema.json", + "EPhWFgeOy8g7yRy-Xtyvbdieqvl_3YVXNHMgTEZuJOWh": "ai-coder/ai-coder.schema.json", + "EBCnd7qk82wLBOgFukdmsdkksAuPpmzt5-eg9YKWWP3j": "ai-user-coca/ai-user-coca.schema.json", "EJxFPpyDRV-W6O2Vtjdy2K90ltWmQK8l1jePw5YOo_Ft": "attestation/attestation.schema.json", + "EBxJHMk6MOEUogB6A1rP5x9te7DscPfxFfUGJCkq1Lq5": "award/award.schema.json", + "EFvHYHX0cUx9sdjxZOr9fpPcQKdzRNFH42D8R29p7lAH": "bindkey/bindkey.schema.json", + "EBpGNZSWwj-btOJMJSMLCVoXbtKdJTcggO-zMevr4vH_": "brand-owner/brand-owner.schema.json", + "EDH3Q0MW6oCcwyYw2MN39n1YfPs37o1QEv86kB-fBzmh": "citation/citation.schema.json", + "ELuPsd1mylvD9iY_gH5hEJzoUd8OZWtU6J2svJwIq-TJ": "faa/faa.schema.json", "EKrv2S0OVc8SeKCzIAOSE-y4j5ybLOOgB69y12Lzxh6Y": "face-to-face/face-to-face.schema.json", - "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8": "faa/faa.schema.json", - "EPhWFgeOy8g7yRy-Xtyvbdieqvl_3YVXNHMgTEZuJOWh": "ai-coder/ai-coder.schema.json", "EL7irIKYJL9Io0hhKSGWI4OznhwC7qgJG5Qf4aEs6j0o": "gcd/gcd.schema.json", - "EH1jN4U4LMYHmPVI4FYdZ10bIPR7YWKp8TDdZ9Y9Al-P": "vvp-dossier/vvp-dossier.schema.json", - "EAoRVmgPyacjhUxaV0nPwiuUuHMjKDpNZrj7ClofZ-3Z": "ovc-brand-owner/ovc-brand-owner.schema.json", "EJvwY9n7EsJ4ZejUBHFrnrNammC8BkGI9YaW1Wnp5c22": "org-vet/org-vet.schema.json", - "EFvHYHX0cUx9sdjxZOr9fpPcQKdzRNFH42D8R29p7lAH": "bindkey/bindkey.schema.json", + "EAoRVmgPyacjhUxaV0nPwiuUuHMjKDpNZrj7ClofZ-3Z": "ovc-brand-owner/ovc-brand-owner.schema.json", "EHFdm3U_4nML6lo-q_xDTO8183hC9HlWif2l4ycNo8TW": "ovc-org-vet/ovc-org-vet.schema.json", - "EBpGNZSWwj-btOJMJSMLCVoXbtKdJTcggO-zMevr4vH_": "brand-owner/brand-owner.schema.json", - "EJxFPpyDRV-W6O2Vtjdy2K90ltWmQK8l1jePw5YOo_Ft": "attestation/attestation.schema.json", - "EPy_7LE3tVdl8qEKN5i4L8eAgIM-1I51-DNiewmcq-fe": "tcr-vetting/tcr-vetting.schema.json", "EG68irpfVX667KCLwG85Cn1Mp3sCe38ftARyQJrxP2kF": "proof-of-control/proof-of-control.schema.json", - "EBxJHMk6MOEUogB6A1rP5x9te7DscPfxFfUGJCkq1Lq5": "award/award.schema.json", - "EDH3Q0MW6oCcwyYw2MN39n1YfPs37o1QEv86kB-fBzmh": "citation/citation.schema.json", + "EPy_7LE3tVdl8qEKN5i4L8eAgIM-1I51-DNiewmcq-fe": "tcr-vetting/tcr-vetting.schema.json", + "EFvnoHDY7I-kaBBeKlbDbkjG4BaI0nKLGadxBdjMGgSQ": "tn-alloc/tn-alloc.schema.json", "EGEebb1pVRcZ6OXHlYitl5DNh-LDrMWPwRtstiKiDhRy": "tn/tn.schema.json", - "EBCnd7qk82wLBOgFukdmsdkksAuPpmzt5-eg9YKWWP3j": "ai-user-coca/ai-user-coca.schema.json", - "EJrcLKzq4d1PFtlnHLb9tl4zGwPAjO6v0dec4CiJMZk6": "org-vet/org-vet.schema.json", - "EKR1nFV0JvRhc4xkhzlZJfpN_taaspipcZWWSLj37Fdk": "aegis-std-vetting/aegis-std-vetting.schema.json", - "EMhvwOlyEJ9kN4PrwCpr9Jsv7TxPhiYveZ0oP3lJzdEi": "vLEI/acdc/verifiable-ixbrl-report-attestation.json", - "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy": "vLEI/acdc/legal-entity-official-organizational-role-vLEI-credential.json", + "EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g": "vLEI/acdc/ecr-authorization-vlei-credential.json", "EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw": "vLEI/acdc/legal-entity-engagement-context-role-vLEI-credential.json", - "ECYorXkheU7YsXZkYLGtvBOxEZ6alS5H5FJRn0tgDXV0": "vLEI/acdc/verifiable-ixbrl-report-d6-attestation.json", + "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy": "vLEI/acdc/legal-entity-official-organizational-role-vLEI-credential.json", "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY": "vLEI/acdc/legal-entity-vLEI-credential.json", + "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E": "vLEI/acdc/oor-authorization-vlei-credential.json", "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao": "vLEI/acdc/qualified-vLEI-issuer-vLEI-credential.json", - "EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g": "vLEI/acdc/ecr-authorization-vlei-credential.json", - "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E": "vLEI/acdc/oor-authorization-vlei-credential.json" + "EMhvwOlyEJ9kN4PrwCpr9Jsv7TxPhiYveZ0oP3lJzdEi": "vLEI/acdc/verifiable-ixbrl-report-attestation.json", + "ECYorXkheU7YsXZkYLGtvBOxEZ6alS5H5FJRn0tgDXV0": "vLEI/acdc/verifiable-ixbrl-report-d6-attestation.json", + "EH1jN4U4LMYHmPVI4FYdZ10bIPR7YWKp8TDdZ9Y9Al-P": "vvp-dossier/vvp-dossier.schema.json" } \ No newline at end of file diff --git a/tools/cli/register_all.py b/tools/cli/register_all.py index 0d4e1d3..cf58a60 100644 --- a/tools/cli/register_all.py +++ b/tools/cli/register_all.py @@ -56,8 +56,9 @@ def register_all(args): id = get_id(schema_file) schemas[id] = f"{vlei_dir}/{item}" + sorted_schemas = dict(sorted(schemas.items(), key=lambda item: item[1])) with open('registry.json', 'w') as f: - json.dump(schemas, f, indent=2) + json.dump(sorted_schemas, f, indent=2) def main(): args = parser.parse_args() From 4884a25d46743b0ef56f68099b6039c4ea59670d Mon Sep 17 00:00:00 2001 From: Daniel Hardman Date: Tue, 17 Mar 2026 13:50:40 -0600 Subject: [PATCH 13/13] remove useless backup file Signed-off-by: Daniel Hardman --- faa/example.json.new.bak | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 faa/example.json.new.bak diff --git a/faa/example.json.new.bak b/faa/example.json.new.bak deleted file mode 100644 index 0f9830d..0000000 --- a/faa/example.json.new.bak +++ /dev/null @@ -1,24 +0,0 @@ -{ - "v": "ACDC10JSON000000_", - "d": "", - "i": "EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL", - "ri": "EM2YZ78SKE8eO4W1lQOJeer5xKZqLmJV7SPr3Ji5DMBZ", - "s": "EF1f9-7xwdba4L3b2OsnhUMWb02NyXJAYJ34RrAFkHs8", - "a": { - "d": "EFM0s2YlM32LAh0h77tvkz3OR6AaYmHcVchm9oVNkvbI", - "u": "0ADY50r_UJ3OaDC9ww7UcwXM", - "dt": "2025-03-01T14:22:00.000000+00:00", - "content_identifier": "EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp", - "art_posture": "witness", - "rev_latency": 0, - "content_type": "image/jpeg", - "content_size": 2847392, - "content_location": [ - "https://evidence.example.org/artifacts/crash-scene-photo-01.jpg", - "https://backup.example.org/artifacts/crash-scene-photo-01.jpg" - ], - "filename": "crash-scene-photo-01.SAID:EK2r6EnDXre2pecTBO8s99j4OtNaaDIhVyr7uGugDhmp.jpg", - "description": "Photograph of the intersection of Main St and 5th Ave taken from the northwest corner, showing vehicle positions immediately after the collision.", - "provenance": "Captured by traffic camera AID EC4SuEyzrRwu3FWFrK0Ubd9xejlo5bUwAtGcbBGUk2nL at 2025-03-01T14:19:43Z and downloaded by the insurance adjustor at 2025-03-01T14:22:00Z." - } -} \ No newline at end of file