A protocol for single-file AI agent distribution, identity,
and collection without central infrastructure
We propose RAPP — the RAPP Agent Registry — a protocol for distributing AI agents as single Python files with deterministic, manifest-derived card identities. Every agent is one file. Every file contains a structured manifest that can be extracted without executing any code. Every manifest produces a 64-bit seed. Every seed, applied to a fixed resolution algorithm, reconstructs a complete card identity: type, stats, abilities, rarity, and flavor — identical across every node that holds the file. No server is required to read the registry. No server is required to resolve a card. The protocol is fully operational from a static file system, a local git clone, or a browser loading resources from file://. The registry grows through federated GitHub repositories governed by the same protocol specification. This document defines the protocol in full.
AI agents exist. The problem is not capability — the problem is distribution. To use an AI agent today, a developer must navigate API tokens, SDK versioning, dependency managers, container runtimes, and cloud billing dashboards. A researcher must clone repositories with dozens of transitive dependencies and hope the Python version matches. A teacher must understand what a virtual environment is before they can run the first line.
This is not a tooling failure. It is an architectural failure. The unit of AI distribution is currently a service — hosted, gated, metered, ephemeral. Services require infrastructure to exist. Infrastructure requires operators. Operators can disappear. When they do, the agents go with them.
The correct unit of AI distribution is a file.
A file can be copied. A file can be emailed. A file can be stored on a USB drive and handed to someone who has never connected to the internet. A file does not require a server to exist. A file does not expire. A file is the most durable, portable, universally-understood artifact in computing.
RAPP is built on this premise. It defines a protocol where every AI agent is one Python file, every file is self-describing through an embedded manifest, and every manifest deterministically produces a card identity that travels with the file forever. The registry is a static JSON document. The store is a single HTML file. The entire system works offline.
The goal is a distribution model that scales to all of humanity — not just engineers at technology companies.
The protocol mandates a strict structure. Each agent is exactly one .py file. The path convention is agents/@publisher/agent_slug_agent.py — lowercase, snake_case slug, required _agent.py suffix. No subdirectories. No companion files. No build artifacts.
The file contains four required components, in order:
The module-level docstring is the agent's documentation. It is free-form text. There is no separate README. There is no wiki page. The docstring is the record.
__manifest__ dictA module-level Python dict named __manifest__ containing structured metadata. This dict is the package manifest, the card specification, and the identity anchor — all in one. It is extracted via Abstract Syntax Tree (AST) parsing. The file is never imported. The code is never executed during registration. This means a malicious agent cannot influence the registry by executing code at import time.
BasicAgentThe agent class must inherit from BasicAgent imported from agents.basic_agent. This is the base class in the CommunityRAPP ecosystem. It provides the runtime harness, the name attribute, and the standard lifecycle hooks.
perform() methodThe single required method. It accepts keyword arguments and returns a str. Always. No exceptions, no other return types. The method is the contract.
""" My Agent — does one thing, does it well. One sentence description here. """ __manifest__ = { "schema": "rapp-agent/1.0", "name": "@publisher/my_agent", "version": "1.0.0", "display_name": "MyAgent", "description": "One sentence.", "author": "Your Name", "tags": ["keyword"], "category": "productivity", "quality_tier": "community", "requires_env": [], "dependencies": ["@rapp/basic_agent"], } from agents.basic_agent import BasicAgent class MyAgent(BasicAgent): def __init__(self): self.name = "MyAgent" def perform(self, **kwargs) -> str: return "result"
The constraints are load-bearing. No network calls in __init__() — constructors must be fast for agent loading. Secrets via os.environ.get(), declared in requires_env, never hardcoded. display_name in the manifest must match self.name in the class. These rules are enforced at build time and in the contract test suite.
The file is the package, the manifest, and the documentation. It has no dependencies on this registry. It runs anywhere Python 3.11+ runs.
The __manifest__ dict conforms to schema version rapp-agent/1.0. The following fields are required. A build failure occurs if any are absent or invalid.
| Field | Type | Description |
|---|---|---|
schema | string | Must be "rapp-agent/1.0" |
name | string | @publisher/slug — publisher namespace + snake_case slug |
version | string | Semantic version: MAJOR.MINOR.PATCH |
display_name | string | Human-readable name. Must match self.name in the class. |
description | string | One sentence. The card description. |
author | string | Author name or handle |
tags | list[str] | Keyword tags for discovery and ability selection |
category | string | One of: core, pipeline, integrations, productivity, devtools |
The following fields are optional with defaults:
| Field | Default | Description |
|---|---|---|
quality_tier | "community" | One of: experimental, community, verified, official |
requires_env | [] | List of required environment variable names |
dependencies | [] | List of @publisher/slug agent dependencies |
The manifest is extracted using Python's ast module. The file is parsed into an abstract syntax tree and the __manifest__ assignment is located and evaluated as a literal — using ast.literal_eval(), which handles only Python literals: strings, numbers, lists, dicts, booleans, and None. No code can be injected or executed through the manifest. This is the safety foundation on which the entire registry rests.
registry.json is the machine-readable index of all agents. It is auto-generated by build_registry.py — the only build step in the system. It is never hand-edited. CI overwrites it on every push to agents/**.
For each agent, the registry stores:
The registry is a static file. It is served via GitHub Pages CDN. It is committed directly to the repository. Any node that clones the repository has the complete registry with no network dependency. Any node can rebuild the registry from the agent files alone — the registry is a derived artifact, never the source of truth.
The source of truth is the agent files.
Version immutability is enforced at the registry level: a @publisher/slug@1.0.0 entry, once written, is never overwritten. A new version requires a new version string. This guarantees that any node holding a registry snapshot can verify historical file integrity against historical hashes indefinitely.
The seed is the core innovation of the RAPP protocol. It is a 64-bit integer forged deterministically from the agent's manifest data. Given the same manifest, the same seed is always produced — on any machine, in any timezone, across any version of the registry software. The seed encodes the agent's identity, type, tier, and capability fingerprint in a single integer that can be stored anywhere, transmitted in any format, and used to reconstruct the full card without any network access.
The 64-bit seed is composed of the following fields packed into specific bit ranges:
@publisher/slug). This is the identity anchor.
def forge_seed(manifest: dict) -> int: name = manifest["name"] # "@publisher/slug" category = manifest["category"] tier = manifest.get("quality_tier", "community") tags = manifest.get("tags", []) deps = manifest.get("dependencies", []) name_hash = fnv1a_32(name) category_idx = CATEGORY_ORDER.index(category) if category in CATEGORY_ORDER else 0 secondary = derive_secondary_type(tags) tier_idx = TIER_ORDER.index(tier) if tier in TIER_ORDER else 0 tag_count = min(len(tags), 31) dep_count = min(len(deps), 15) tag_hash = fnv1a_32(",".join(sorted(tags))) & 0x1FFF seed = (name_hash & 0xFFFFFFFF) << 32 seed |= (category_idx & 0x1F) << 27 seed |= (secondary & 0x07) << 24 seed |= (tier_idx & 0x03) << 22 seed |= (tag_count & 0x1F) << 17 seed |= (dep_count & 0x0F) << 13 seed |= (tag_hash & 0x1FFF) return seed
Three independent paths produce identical card output. This is the losslessness guarantee of the protocol:
| Input | Path | Output |
|---|---|---|
| Agent file | AST-extract manifest → forge_seed() → resolve_card_from_seed() |
Full card |
| Agent name | Lookup registry by name → read forged seed → resolve_card_from_seed() |
Full card |
| Seed integer | resolve_card_from_seed() directly |
Full card |
Any node holding any one of these three things — the file, the name, or the seed — can reconstruct the complete card. The card is not stored. The card is derived. This distinction is the difference between a system that can fail and a system that cannot.
Every agent card belongs to one of seven elemental types. Type is derived from the agent's category field and secondary tag composition. The types form a closed weakness/resistance cycle — each type is strong against one and resistant to one.
| Type | Color | Category Basis | Character |
|---|---|---|---|
| LOGIC | #58a6ff | devtools, core | Systematic, precise, structural |
| DATA | #3fb950 | pipeline | Transformative, patient, deep |
| #e3b341 | integrations | Connected, adaptive, communicative | |
| SHIELD | #e6edf3 | security, compliance | Protective, vigilant, stable |
| CRAFT | #f85149 | productivity, creative | Generative, inventive, expressive |
| HEAL | #ff9ecb | wellness, support | Restorative, empathic, durable |
| WEALTH | #bc8cff | finance, economy | Accumulative, strategic, leveraged |
The cycle is: LOGIC > DATA > > SHIELD > CRAFT > HEAL > WEALTH > LOGIC.
Each type is weak to the type before it in the cycle and resistant to the type after it. This creates a balanced rock-paper-scissors equilibrium across all seven types — no type dominates, every type has a natural counter.
Dual-typed cards (where the secondary_type bits are non-zero) carry both the weakness of their primary type and a modified resistance that blends both types. Dual-typed cards are rarer and arise when an agent's tag composition crosses category boundaries in the manifest.
A resolved card has the following structure. Every field is deterministic from the seed. No field is stored. No field can be changed by the agent author after the manifest is sealed.
| Field | Range | Derivation |
|---|---|---|
| HP | 10 – 100 | Scaled from name_hash × tier_multiplier |
| ATK | 10 – 100 | Derived from category_idx × tag_count |
| DEF | 10 – 100 | Derived from dep_count and tier_idx |
| SPD | 10 – 100 | Inverse of dep_count (fewer deps = faster) |
| INT | 10 – 100 | tag_hash spread across full range |
| Abilities | 1 – 3 | Selected from type-specific pool by tag_hash |
| Weakness | type | Fixed from primary type cycle |
| Resistance | type | Fixed from primary type cycle |
| Retreat Cost | 0 – 3 | Derived from dep_count |
| Evolution Stage | Seed/Base/Evolved/Legendary | Directly from tier_idx |
| Rarity | Starter/Core/Elite/Legendary | Directly from tier_idx |
| Flavor Text | string | Selected from description-keyed template pool |
Each ability has three fields: a name, an energy cost (0–3), and a damage value. Abilities are drawn from a type-keyed pool of hundreds of named abilities. The selection is deterministic: the tag_hash bits index into the pool modulo its length. The same tag composition always produces the same ability set.
Flavor text is selected from a pool of templates keyed to the agent's description. The selection is hash-stable. Two agents with similar descriptions may share flavor templates but will always produce different flavor text because the template parameters are drawn from their respective seeds.
Agent submission is a two-step process. The protocol uses GitHub Issues as its API surface — no custom infrastructure, no application server, no authentication service beyond GitHub's own.
The submitter opens a GitHub Issue with the title prefix [RAR] register_binder and a body containing their publisher handle, display name, and a brief description of their namespace. The process-issues.yml workflow fires on issue open, validates the handle format (@publisher, alphanumeric plus hyphens, no reserved names), and acknowledges the registration. The binder is now associated with the publisher's GitHub identity.
The submitter opens a second Issue with the prefix [RAR] submit_agent and attaches or pastes their agent file. The automation validates the file against the protocol contract: manifest presence, required fields, naming convention, AST extractability, security scan pass. On validation success, the file is written to staging. A human reviewer is notified.
This is the most important security property of the submission protocol: the submitter does not choose their card attributes. The forge algorithm determines everything. A submitter who wants a Legendary rarity card cannot write that into the manifest — the quality_tier field is validated by the reviewer and defaults to community. Tier promotions are granted by the registry maintainers, not requested by authors.
This separates identity from authority. An agent is what its code says it is. Its card is what the protocol says it is. The author owns the code. The protocol owns the card.
Submitted agents land in a staging namespace. They are visible in the registry but clearly marked as pending. The reviewer checks: does the agent do what the description claims? Does it handle errors gracefully? Are secrets managed correctly? Are there any security red flags the static scanner missed? On approval, the agent moves to its permanent namespace and the registry is rebuilt.
RAPP is a GitHub template repository. Any organization or individual can create a federated instance by cloning the template. The result is a fully operational agent registry — with its own namespace, its own CI/CD, its own GitHub Pages deployment — in under five minutes.
A federated instance can operate in any of four modes:
| Mode | Description |
|---|---|
| Standalone | Independent registry. No upstream sync. Full local control. |
| Downstream | Pulls agents from the upstream registry. Inherits the founding agent set. |
| Upstream-contributing | Can submit agents to the upstream registry for official forging. |
| Peer | Mutual sync with another instance. Shared namespace agreements. |
Federation configuration lives in rar.config.json. The upstream URL, sync policy, namespace reservations, and feature flags are all defined there. The template automation reads this file on first setup and configures the repository accordingly.
A binder (personal card collection) is the individual counterpart to the registry. A binder is stored in the browser's IndexedDB and localStorage. It can be exported as a JSON file and imported on any other device or any other instance. A binder is not tied to any server. A binder travels with the person.
The community grows grassroots. Every fork is a node. Every node is a registry. Every registry can contribute upstream. No single node is required for the system to function.
The seed is public. Anyone can resolve a card from its seed. The stats, types, abilities, weakness, and rarity are visible to everyone. This is by design — the card's identity is open, just like a trading card's printed face is visible to anyone who looks at it.
Ownership is private. A card lives in exactly one binder at a time. Ownership is recorded in the binder's local state — not on any public server, not in the registry, not in the seed. The binder is the deed.
This separation is fundamental:
When a card is transferred, it leaves one binder and enters another. The seed does not change — the identity is permanent. Only the location changes. This mirrors physical trading cards: the printed stats are the same in every collection, but the card itself is in one person's binder.
Using an agent is always free. Ownership is the collectible layer — it does not gate access. Anyone can download, run, and modify any agent. Owning the card is something else entirely: it is provenance, identity, and membership in the collection.
The seed is the face of the card. The binder is the hand that holds it.
The security model is built on four layers: static analysis, AST-only parsing, integrity hashing, and namespace access control.
Every submitted file is scanned before registration. The scanner rejects files containing any of the following patterns:
eval( or exec( — arbitrary code executionsubprocess — shell command executionos.system( — shell command execution__import__( — dynamic import bypassperform() method bodyFirst-party agents in the @rapp namespace and a maintained allowlist of verified publishers may use elevated capabilities with explicit declaration in the manifest's requires_env field.
The manifest is extracted using ast.literal_eval(). This function evaluates only Python literals — strings, numbers, booleans, lists, dicts, tuples, and None. It cannot execute function calls, access variables, import modules, or run any code. A manifest that attempts to set a field to os.environ["SECRET"] will fail extraction. This is the strongest possible guarantee against manifest injection.
The registry stores a SHA256 hash of each agent file at registration time. Any consumer of the registry can verify that the file they hold matches the registered version. This prevents silent file tampering between the registry and the consumer.
CODEOWNERS in the repository enforces that only the registered owner of a namespace can merge changes under that path. A pull request to agents/@kody/ from a non-@kody identity will be blocked by the branch protection rules. Namespace squatting — registering a handle to block a legitimate publisher — is addressed by the binder registration process, which requires a GitHub identity match.
Every read operation in the RAPP protocol works without a network connection. This is not a feature — it is a design constraint. Any system that requires network access to read its own data is a system that can fail when the network is unavailable. The network is always, eventually, unavailable.
| Operation | Network required? | How it works |
|---|---|---|
| Browse the registry | No | registry.json is a local file in the clone |
| Resolve a card | No | Seed algorithm runs locally, zero bandwidth |
| Open the agent store | No | index.html is a single file, works from file:// |
| View the binder | No | IndexedDB + localStorage, browser-native |
| Read agent documentation | No | Docstring is in the file |
| Run an agent | No* | The file is the agent (* unless the agent calls external APIs) |
Submitting a new agent, voting on a review, and registering a binder all require GitHub access. These are write operations — they modify the shared state of the registry. But the offline read path is unaffected. A node can hold and use the full registry indefinitely without ever writing to it.
The binder uses IndexedDB as its primary storage and localStorage as a fallback. The card collection, deck configurations, and favorites are all stored locally. The binder can be exported as a portable JSON file — a snapshot of the collection at any point in time — and imported on any device without any server interaction.
A git clone is a full node. The file is the agent. The seed is the card. Nothing else is required.
Every agent begins its life as experimental. Promotion through the tiers is earned through review, adoption, and demonstrated reliability. The tiers map directly to card evolution stages — an agent's card grows as the agent grows.
| Tier | Badge | Stage | Rarity | Promotion Path |
|---|---|---|---|---|
| experimental | stage 0 | Seed | Starter | Submit via Issues. Automated validation pass. |
| community | stage 1 | Base | Core | Human review. Agent does what it claims. No security flags. |
| verified | stage 2 | Evolved | Elite | Maintainer review. Test coverage. Adoption evidence. Standards compliance. |
| official | stage 3 | Legendary | Legendary | Core team maintained. Foundational to the ecosystem. Long-term stability commitment. |
Tier promotions are encoded in the manifest's quality_tier field and reflected immediately in the forged seed. When an agent is promoted from community to verified, its seed changes — tier_idx increments — and its card evolves. The previous card is not destroyed; it exists in the registry history as a sealed version. The new card is the current form.
This maps naturally to the trading card concept of evolution. A Seed-stage card is full of potential. A Legendary card is a proven, load-bearing piece of infrastructure. Both are real. Both have value. The evolution is earned, not purchased.
The founding set — the 131 agents registered at protocol launch on April 5, 2026 — are the genesis cards. They cannot be demoted, and their card identities at launch are sealed in the immutable registry history. They are the equivalent of the first issue of a series: whatever they become later, their origin is permanent.
A rapp egg is an entire Brainstem environment compressed to seeds. Each agent is a 64-bit integer. An array of 20 agent seeds is 160 bytes — 217 characters in base64. This fits in a single QR code.
The egg is the recipe, not the meal. The recipient's client reads the seeds, resolves each one, downloads the agent files if needed, and reconstructs the complete environment. The data self-assembles on the other end.
| Agents | Raw bytes | Compact string | Medium |
|---|---|---|---|
| 5 | 40 B | 54 chars | SMS, NFC tap |
| 20 | 160 B | 217 chars | QR code, tweet |
| 50 | 400 B | 537 chars | QR code |
| 100 | 800 B | 1,070 chars | QR code, email |
The egg format supports three tiers of payload, each at a different bandwidth level:
This is sneakernet at its purest. Two people in a room with no internet can trade entire AI environments by exchanging a short string. The string carries the seeds. The seeds carry the cards. The cards carry the agents. The agents carry the intelligence.
The seed protocol is not limited to agent cards. It is a general principle:
Any structured content with a defined schema can be packed into a seed and reconstructed from that seed, anywhere, offline, with zero data transfer.
The requirements are:
Minecraft does not store every block in a world. It stores a seed that the terrain generator uses to place every block. The world is "custom" but it is derived from the seed because the protocol (terrain generation rules) is shared by every client. The seed is the compressed data. The protocol is the decompressor.
The RAPP card seed protocol demonstrates this for agent identity. The same principle extends to:
The key constraint: define the data structure first. The schema is the protocol. Without the protocol, the seed is just a number. With the protocol, the seed is an entire world. This is why the card schema was locked before launch — once seeds are in the wild, the protocol that resolves them must be permanent.
The protocol is defined. The registry is live. The seed algorithm is public. No single entity is required to operate the system.
Any person with Python 3.11 and a text editor can write an agent that is fully compliant with this protocol. Any person with git can hold the complete registry. Any person with a browser can resolve any card without contacting any server. Any organization with a GitHub account can operate a federated instance. Two people with a QR code can trade entire AI environments without an internet connection.
The file is the agent. The manifest is the identity. The seed is the card. The egg is the environment. The registry is the index. The store is the interface. The binder is the collection. All of it runs from a local directory. All of it works when the network is down. None of it requires a company to exist for it to keep working.
This is not a product. It is a protocol. Protocols outlast products. The internet outlasted every ISP that helped build it. Bitcoin outlasted every exchange that said it needed. The RAPP protocol is designed to the same standard: define the rules clearly enough that the rules enforce themselves, and then step back.
Fork the repository. Run your own instance. Federate when ready. Write agents. The file is the future.