Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions agent-passbook-prototype/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Agent Passbook Prototype

Prototype repository for tracking agent-issued passbooks within the NANDA organization. This project defines a JSON schema for passbook records, provides a sample passbook, and includes a small resolver utility to inspect passbook files.

## Layout
- `schemas/passbook.json`: JSON Schema describing a passbook document.
- `examples/example-passbook-agentA.json`: Example passbook document adhering to the schema.
- `service/passbook-resolver.py`: Utility for loading and summarizing passbook files.

## Usage
```bash
python service/passbook-resolver.py examples/example-passbook-agentA.json
```
The resolver reports validation status and prints a concise summary of the passbook contents.
24 changes: 24 additions & 0 deletions agent-passbook-prototype/examples/example-passbook-agentA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "passbook-agentA-0001",
"agent": {
"name": "Agent A",
"unit": "Reconnaissance",
"contact": "[email protected]"
},
"issued_at": "2024-06-01T12:00:00Z",
"entries": [
{
"timestamp": "2024-06-02T09:15:00Z",
"event": "Passbook issued",
"authority": "HQ Registrar",
"notes": "Initial issuance"
},
{
"timestamp": "2024-06-05T14:32:00Z",
"event": "Clearance verified",
"authority": "Operations Control",
"notes": "Validated for field deployment"
}
],
"checksum": "c0ffee89"
}
64 changes: 64 additions & 0 deletions agent-passbook-prototype/schemas/passbook.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://nanda.org/agent-passbook.schema.json",
"title": "Agent Passbook",
"type": "object",
"required": ["id", "agent", "issued_at", "entries"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for this passbook"
},
"agent": {
"type": "object",
"required": ["name", "unit"],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"unit": { "type": "string" },
"contact": {
"type": "string",
"description": "Preferred contact channel for the issuing agent"
}
}
},
"issued_at": {
"type": "string",
"format": "date-time",
"description": "ISO timestamp when the passbook was issued"
},
"entries": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["timestamp", "event", "authority"],
"additionalProperties": false,
"properties": {
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO timestamp for the event"
},
"event": {
"type": "string",
"description": "Short description of the event"
},
"authority": {
"type": "string",
"description": "Person or system authorizing the entry"
},
"notes": {
"type": "string",
"description": "Optional supporting detail"
}
}
}
},
"checksum": {
"type": "string",
"description": "Optional checksum for integrity verification"
}
}
}
113 changes: 113 additions & 0 deletions agent-passbook-prototype/service/passbook-resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""Simple resolver to load and validate agent passbook files."""

import argparse
import json
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Load and validate a passbook file.")
parser.add_argument(
"passbook",
type=Path,
help="Path to the passbook JSON file",
)
parser.add_argument(
"--schema",
type=Path,
default=Path(__file__).resolve().parent.parent / "schemas" / "passbook.json",
help="Path to the passbook JSON schema (for reference)",
)
return parser.parse_args()


def load_json(file_path: Path) -> Dict[str, Any]:
with file_path.open("r", encoding="utf-8") as handle:
return json.load(handle)


def validate_datetime(value: str) -> bool:
try:
datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError:
return False
return True


def validate_passbook(document: Dict[str, Any]) -> List[str]:
errors: List[str] = []

for field in ("id", "agent", "issued_at", "entries"):
if field not in document:
errors.append(f"Missing required field: {field}")

if "agent" in document:
agent = document["agent"]
if not isinstance(agent, dict):
errors.append("agent must be an object")
else:
for field in ("name", "unit"):
if field not in agent:
errors.append(f"agent.{field} is required")

if "issued_at" in document and not validate_datetime(str(document["issued_at"])):
errors.append("issued_at must be an ISO 8601 timestamp")

entries = document.get("entries")
if not isinstance(entries, list) or len(entries) == 0:
errors.append("entries must be a non-empty array")
else:
for index, entry in enumerate(entries):
if not isinstance(entry, dict):
errors.append(f"entries[{index}] must be an object")
continue
for field in ("timestamp", "event", "authority"):
if field not in entry:
errors.append(f"entries[{index}].{field} is required")
timestamp = entry.get("timestamp")
if timestamp is not None and not validate_datetime(str(timestamp)):
errors.append(f"entries[{index}].timestamp must be an ISO 8601 timestamp")

return errors


def summarize_passbook(document: Dict[str, Any]) -> str:
agent = document.get("agent", {})
entries = document.get("entries", [])
issued_at = document.get("issued_at", "unknown time")
lines = [
f"Passbook: {document.get('id', 'unknown id')}",
f"Agent: {agent.get('name', 'unknown name')} (unit: {agent.get('unit', 'unknown')})",
f"Issued at: {issued_at}",
"Entries:",
]
for entry in entries:
lines.append(
f" - {entry.get('timestamp', 'unknown time')}: {entry.get('event', 'unknown event')}"
f" (authority: {entry.get('authority', 'unknown')})"
)
return "\n".join(lines)


def main() -> None:
args = parse_args()
document = load_json(args.passbook)
errors = validate_passbook(document)

print(f"Loaded passbook from {args.passbook}")
print(f"Schema reference: {args.schema}")

if errors:
print("Validation: FAILED")
for issue in errors:
print(f" - {issue}")
else:
print("Validation: PASSED")
print(summarize_passbook(document))


if __name__ == "__main__":
main()