UAAPS supports two registry types that share the same package format (.aam archives) but differ in transport:
| Type | When to use |
|---|---|
| Local filesystem | Offline use, air-gapped environments, CI mirrors, monorepos |
| HTTP remote | Public registry, private org registry, hosted SaaS |
The aam CLI auto-detects the registry type from the URL scheme:
# HTTP remote registry
aam install @myorg/code-review --registry https://aamregistry.io
# Local filesystem registry
aam install @myorg/code-review --registry file:///opt/aam-registry
# Default registry (HTTP, configured in ~/.aam/config.yaml)
aam install @myorg/code-reviewRegistry configuration is managed in ~/.aam/config.yaml or .aam/config.yaml:
registries:
default: https://aamregistry.io # planned public registry — see §12.6
sources:
- name: myorg-remote
url: https://pkg.myorg.internal/aam
auth: token # see §12.6 for auth types
- name: myorg-local
url: file:///opt/aam-registryOther distribution channels (Git marketplace, npm bridge, direct install, project-bundled) are supported by the
aamCLI — seeaam_cli_new.md.
Packages are distributed as .aam archives (gzipped tar), providing a binary-safe, single-file distribution unit.
my-package-1.0.0.aam # produced by: aam pkg pack
Archive constraints:
- Maximum size: 50 MB
- MUST contain
package.agent.jsonat root - No symlinks outside the package directory
- No absolute paths
- SHA-256 checksum stored in
package.agent.lockpost-install
The archive packlist MUST be deterministic.
Selection algorithm:
- If
package.agent.jsondefinesfiles, start from the union of those glob matches. - Otherwise, start from all files under the package root.
- Always include
package.agent.json. - Always include
README,README.md,LICENSE,LICENSE.md,CHANGELOG, andCHANGELOG.mdwhen present. - Always exclude these paths from the candidate set:
.git/**,.hg/**,.svn/**.agent-packages/**,node_modules/**.venv/**,venv/**,__pycache__/**,*.pyc.DS_Store,Thumbs.dbpackage.agent.lockevals/reports/**
Packlist rules:
- Archive entries MUST use forward-slash paths relative to the package root.
- Archive entries MUST be sorted lexicographically by path before tar creation.
aam pkg packMUST fail if the final packlist omits a file referenced by the manifest, a declared artifact entry, or a configured hook/MCP path.- Tools SHOULD warn when the packlist includes likely-secret files such as
.env,.env.*, or credential exports.
To support provenance and byte-for-byte rebuilds, archive creation MUST be reproducible.
| Property | Requirement |
|---|---|
| Entry order | MUST be lexicographic by archive path |
| Path format | MUST use / separators only |
| Tar owner/group IDs | MUST be normalized to 0 |
| Tar owner/group names | MUST be empty strings |
| Modification time | MUST be SOURCE_DATE_EPOCH when set, otherwise 0 |
| Gzip header timestamp | MUST match the normalized archive timestamp |
File mode bits MAY be preserved, but implementations MUST normalize any platform-specific metadata not representable across POSIX tar readers.
Following npm convention, packages support a @scope/name format for namespacing under an organization or author:
| Format | Example | Use Case |
|---|---|---|
| Unscoped | code-review |
Community / public packages |
| Scoped | @myorg/code-review |
Organization packages |
Name rules:
- Unscoped:
[a-z0-9-], max 64 chars - Scope:
@[a-z0-9][a-z0-9_-]{0,63} - Full scoped name: max 130 chars
Filesystem mapping — the @scope/name format is converted using double-hyphen:
| Package Name | Filesystem Name |
|---|---|
code-review |
code-review |
@myorg/code-review |
myorg--code-review |
The -- separator is reversible and unambiguous because valid name segments cannot start with hyphens.
Dist-tags are named aliases for package versions, enabling installs like aam install @org/agent@stable or enterprise gates like bank-approved.
| Tag | Behavior |
|---|---|
latest |
Automatically set to the newest published version |
stable |
Opt-in; set manually via aam dist-tag add |
Organizations can define arbitrary tags (e.g., staging, bank-approved, qa-passed).
Tag rules:
- Lowercase alphanumeric + hyphens only
- Max 32 characters
- Cannot be a valid SemVer string (prevents ambiguity)
A portable bundle is a self-contained, pre-compiled archive for a specific target platform. It contains artifacts already transformed to the platform's native format — no install-time resolution needed.
Use case: Distributing packages via Slack, email, or air-gapped environments without a registry.
aam pkg build --target cursor
# → dist/my-package-1.0.0-cursor.bundle.aam
aam pkg build --target copilot
# → dist/my-package-1.0.0-copilot.bundle.aam
aam pkg build --target all
# → one bundle per configured platformBundle structure (tar.gz internally):
my-package-1.0.0-cursor.bundle.aam
├── bundle.json # Bundle manifest
├── .cursor/
│ ├── skills/... # Pre-compiled platform artifacts
│ ├── rules/...
│ └── commands/...
└── package.agent.json # Original manifest for reference
bundle.json schema:
{
"format": "uaaps-bundle",
"version": "1.0",
"package": "@author/my-agent",
"package_version": "1.2.0",
"target": "cursor",
"built_at": "2026-02-19T14:30:00Z",
"checksum": "sha256:...",
"artifacts": [
{ "type": "skill", "name": "my-skill", "path": ".cursor/skills/author--my-skill/" },
{ "type": "agent", "name": "my-agent", "path": ".cursor/rules/agent-author--my-agent.mdc" }
]
}Installing from a bundle:
aam install ./dist/my-package-1.0.0-cursor.bundle.aam
# Deploys immediately — no dependency resolution neededA local filesystem registry is a directory tree on disk following a defined layout. No server process is required. It is suitable for offline installs, CI artifact mirrors, and air-gapped enterprise environments.
<registry-root>/
├── index.json # Full package index (all packages + versions)
├── packages/
│ ├── code-review/ # Unscoped package
│ │ ├── meta.json # Package metadata (all versions)
│ │ └── versions/
│ │ ├── 1.0.0.aam
│ │ ├── 1.0.0.aam.sha256 # Detached SHA-256 checksum
│ │ ├── 1.1.0.aam
│ │ └── 1.1.0.aam.sha256
│ └── myorg--code-review/ # Scoped package (@myorg/code-review)
│ ├── meta.json
│ └── versions/
│ ├── 2.0.0.aam
│ └── 2.0.0.aam.sha256
└── dist-tags.json # Global dist-tag → version map
A flat list of all packages in the registry. Implementations MUST regenerate this file after every publish or lifecycle state mutation.
{
"formatVersion": 1,
"updatedAt": "2026-02-22T14:00:00Z",
"packages": [
{ "name": "code-review", "latest": "1.1.0", "versions": ["1.0.0", "1.1.0"] },
{ "name": "@myorg/code-review", "latest": "2.0.0", "versions": ["2.0.0"] }
]
}Per-package metadata covering all published versions.
{
"name": "@myorg/code-review",
"versions": {
"2.0.0": {
"version": "2.0.0",
"description": "Code review skills for myorg",
"author": "myorg",
"publishedAt": "2026-02-20T10:00:00Z",
"integrity": "sha256-abc123...",
"tarball": "versions/2.0.0.aam"
}
},
"dist-tags": {
"latest": "2.0.0",
"stable": "2.0.0"
}
}Registry-wide dist-tag snapshot for fast tag resolution without reading every meta.json.
{
"code-review": { "latest": "1.1.0", "stable": "1.0.0" },
"@myorg/code-review": { "latest": "2.0.0" }
}| Rule | Requirement |
|---|---|
Package directory name MUST use the scope--name mapping (§12.2) |
MUST |
Every .aam file MUST have a sibling .aam.sha256 file |
MUST |
index.json MUST be regenerated atomically after each mutation |
MUST |
| Symlinks outside the registry root are forbidden | MUST NOT |
| The registry root MAY be read-only (install-only mirror) | MAY |
# Initialise a new local registry
aam registry init file:///opt/aam-registry
# Publish a package to a local registry
aam pkg publish --registry file:///opt/aam-registry
# Rebuild index.json after manual archive placement
aam registry reindex file:///opt/aam-registry
# List all packages in a local registry
aam registry ls file:///opt/aam-registryStatus: Draft — Endpoint signatures and auth model are defined below. Full request/response schemas, pagination rules, rate-limit headers, and error codes will be detailed in a dedicated Registry Protocol document in a future spec revision. The endpoint list is preliminary and subject to change.
An HTTP registry is an HTTPS server implementing the UAAPS Registry Protocol. The planned official public registry is https://aamregistry.io (under development). Organizations MAY self-host a private registry.
All endpoints are relative to the registry base URL. Implementations MUST serve the API over HTTPS. Plain HTTP MUST NOT be used for registries handling private packages or authentication tokens.
| Method | Path | Purpose | Auth required |
|---|---|---|---|
GET |
/ |
Registry metadata & capabilities | No |
GET |
/packages |
List all packages (paginated) | No (public) / Yes (private) |
GET |
/packages/:name |
Package metadata (all versions) | No (public) / Yes (private) |
GET |
/packages/:name/:version |
Single version metadata | No (public) / Yes (private) |
GET |
/packages/:name/:version/tarball |
Download .aam archive |
No (public) / Yes (private) |
GET |
/packages/:name/:version/signature |
Fetch signature bundle | No |
GET |
/packages/:name/dist-tags |
List dist-tags for package | No |
PUT |
/packages/:name/dist-tags/:tag |
Set a dist-tag | Yes |
DELETE |
/packages/:name/dist-tags/:tag |
Remove a dist-tag | Yes |
POST |
/packages |
Publish a new package version | Yes |
DELETE |
/packages/:name/:version |
Unpublish a version | Yes |
GET |
/approvals |
List pending approval requests | Yes |
POST |
/approvals/:id/approve |
Approve a publish request | Yes |
POST |
/approvals/:id/reject |
Reject a publish request | Yes |
HTTP registries MAY require authentication. The aam CLI supports the following auth types, configured per registry in ~/.aam/config.yaml:
| Auth type | Config value | Transport |
|---|---|---|
| No auth (public) | auth: none |
— |
| Static token | auth: token |
Authorization: Bearer <token> header |
| OIDC / Sigstore keyless | auth: oidc |
Short-lived token via OIDC provider |
| Basic (legacy, not recommended) | auth: basic |
Authorization: Basic <base64> header |
# ~/.aam/config.yaml
registries:
sources:
- name: myorg
url: https://pkg.myorg.internal/aam
auth: token
token: "${MYORG_AAM_TOKEN}" # resolved from environment variableTokens MUST be stored in environment variables or a secrets manager. Tokens MUST NOT be committed to version control. The aam login command handles interactive token acquisition and secure local storage.
aam login --registry https://pkg.myorg.internal/aam # interactive login
aam logout --registry https://pkg.myorg.internal/aam # remove stored credential
aam whoami --registry https://pkg.myorg.internal/aam # show current identityAll error responses MUST use application/json with this structure:
{
"error": {
"code": "PACKAGE_NOT_FOUND",
"message": "Package @myorg/code-review@3.0.0 does not exist.",
"docs": "https://aamregistry.io/errors/PACKAGE_NOT_FOUND"
}
}| Code | Meaning |
|---|---|
200 |
Success |
201 |
Published successfully |
400 |
Malformed request |
401 |
Authentication required |
403 |
Insufficient permissions |
404 |
Package or version not found |
409 |
Version already exists (publish conflict) |
422 |
Validation failed (manifest schema error) |
429 |
Rate limit exceeded |
503 |
Registry temporarily unavailable |
Full pagination headers, rate-limit headers, conditional request support (
ETag,If-None-Match), and approval workflow request/response bodies will be defined in the Registry Protocol document.
Registries MUST use the following machine-readable error codes in the error.code field of error responses. Clients SHOULD use these codes for programmatic error handling rather than relying on HTTP status codes alone.
| Code | HTTP Status | Description |
|---|---|---|
PACKAGE_NOT_FOUND |
404 |
Package name does not exist |
VERSION_NOT_FOUND |
404 |
Specific version does not exist |
VERSION_CONFLICT |
409 |
Version already published |
MANIFEST_INVALID |
422 |
Manifest failed schema validation |
SIGNATURE_INVALID |
403 |
Package signature verification failed |
TAG_NOT_FOUND |
404 |
Dist-tag does not exist |
UNAUTHORIZED |
401 |
Authentication required |
FORBIDDEN |
403 |
Insufficient permissions |
RATE_LIMITED |
429 |
Rate limit exceeded |
REGISTRY_UNAVAILABLE |
503 |
Registry temporarily unavailable |
PACKAGE_YANKED |
410 |
Version is yanked (not available for new resolution) |
PACKAGE_DEPRECATED |
200 |
Version is deprecated (returned alongside valid response) |
Clients SHOULD send an Accept header specifying the versioned media type:
Accept: application/vnd.uaaps.v1+json
Registries MUST also accept application/json as a fallback for clients that do not specify the versioned type. The response Content-Type MUST match the negotiated media type. If the client requests a media type the registry does not support, the registry MUST respond with 406 Not Acceptable.
List endpoints (e.g., GET /packages, GET /approvals) MUST support cursor-based pagination.
Query parameters:
| Parameter | Default | Max | Description |
|---|---|---|---|
cursor |
(none) | — | Opaque token returned by the previous response |
limit |
50 |
200 |
Maximum number of items per page |
Response headers:
| Header | Description |
|---|---|
Link: <url>; rel="next" |
URL for the next page of results |
X-Total-Count |
Total number of items matching the query |
When no more results exist, the registry MUST omit the Link header with rel="next". Clients MUST NOT assume a stable ordering across pages unless a sort parameter is provided.
The following endpoints extend the base endpoint index (§12.6) with search, deprecation, and yank operations.
| Method | Path | Purpose | Auth required |
|---|---|---|---|
GET |
/packages?q=<query>&category=<cat>&sort=<field> |
Search packages | No (public) / Yes (private) |
POST |
/packages/:name/:version/deprecate |
Deprecate a version | Yes |
DELETE |
/packages/:name/:version/deprecate |
Remove deprecation | Yes |
POST |
/packages/:name/:version/yank |
Yank a version | Yes |
DELETE |
/packages/:name/:version/yank |
Undo yank | Yes |
The POST /packages/:name/:version/deprecate endpoint accepts a JSON body:
{
"message": "Use @myorg/agent@2.0.0 instead",
"replacement": "@myorg/agent@^2.0.0"
}The replacement field is OPTIONAL and, when provided, MUST be a valid package specifier. The message field is REQUIRED and MUST NOT exceed 280 characters.
Published packages transition through a defined set of states that control visibility and resolution behavior. UAAPS follows an immutability-first model: once an archive is published, its bytes and version identity are permanent.
deprecate yank
┌───────────┐ ──────────► ┌──────────────┐
│ Published │ │ Deprecated │
└─────┬─────┘ ◄────────── └──────────────┘
│ undeprecate
│
│ yank undo yank
├──────────────────► ┌──────────────┐
│ │ Yanked │
│ ◄──────────────── └──────────────┘
- Published → Deprecated: The
deprecatecommand marks a version as deprecated. Deprecated versions remain fully functional but emit warnings during resolution. - Published → Yanked: The
yankcommand soft-deletes a version. Yanked versions are hidden from search and new resolution but remain downloadable for existing lock files. - There is no hard unpublish. Once published, the archive is permanent.
| State | Appears in search | Resolved by aam install? |
Resolved by --frozen? |
Downloadable? |
|---|---|---|---|---|
| Published | Yes | Yes | Yes | Yes |
| Deprecated | Yes (with warning) | Yes (with warning + replacement message) | Yes | Yes |
| Yanked | No | No | Yes (lock file integrity preserved) | Yes (if in lock file) |
# Deprecate a version with a message
aam deprecate @myorg/agent@1.0.0 "Use @myorg/agent@2.0.0 instead"
# Yank a version (soft-delete)
aam yank @myorg/agent@1.0.0
# Reverse a yank
aam yank --undo @myorg/agent@1.0.0Publishers MAY declare deprecation metadata directly in package.agent.json. When present, the registry SHOULD surface this information in search results and install warnings.
// package.agent.json
{
"deprecated": {
"message": "Use @myorg/agent@2.0.0 instead",
"replacement": "@myorg/agent@^2.0.0"
}
}The message field is REQUIRED when deprecated is present. The replacement field is OPTIONAL and MUST be a valid package specifier when provided.
Unlike npm's
unpublish, UAAPS follows Cargo's immutability model: published archives are permanent. This guarantees that existing lock files always resolve successfully, preventing supply-chain breakage.
Registries MUST reject any attempt to publish different archive bytes for an already-published name@version, even if the prior version is deprecated or yanked.