ALP Specification — Plugin Registry Protocol
Version: 80.0.0 Status: Stable
1. Registry Protocol Overview
flowchart LR
Client[ALP Parser] -->|1. Request| Registry[Plugin Registry]
Registry -->|2. Metadata| Client
Client -->|3. Download| Registry
Registry -->|4. Plugin File| Client
Client -->|5. Cache| Cache[Local Cache]
Cache -->|6. Load| Plugin[Plugin Engine]
Plugin -->|7. Register| Types[Custom Types]
Registry -->|Namespace| NS1[@autonomous-lifecycle-protocol-alp]
Registry -->|Namespace| NS2[@community]2. Overview
While plugins can be imported via direct URLs (e.g., !import: "https://example.com/plugin.alp"), the Plugin Registry Protocol provides a standardized way to discover, host, and import plugins using semantic aliases (e.g., !import: "@autonomous-lifecycle-protocol-alp/scrum@1.0.0").
An ALP Registry is an HTTP service (or static file host) that conforms to a specific directory structure and REST interface, allowing parsers to fetch plugin metadata, resolve versions, and download plugin files.
2. The Registry Alias Syntax
The !import directive supports resolving plugins via a registry alias.
Syntax:
!import: "@<namespace>/<plugin-name>@<version-range>"Examples:
!import: "@autonomous-lifecycle-protocol-alp/scrum@1.0.0" // Exact version
!import: "@autonomous-lifecycle-protocol-alp/kanban@^2.1.0" // Semver range
!import: "@internal/deploy@latest" // Latest tag2.1 Resolution Flow
When a parser encounters a registry alias:
- It determines the registry base URL (from
.alprcconfiguration or falling back to the defaulthttps://registry.alp-protocol.org). - It fetches the plugin metadata from
<base_url>/-/<namespace>/<plugin-name>/meta.json. - It resolves the requested
<version-range>against the available versions in the metadata. - It fetches the actual
.alpfile using the URL provided in the metadata for the resolved version. - It caches the file locally in
.alp/.cache/registry/.
3. Registry API Protocol
A valid ALP registry MUST implement the following HTTP GET endpoints. A registry CAN be implemented as a purely static file server if the files are generated ahead of time.
3.1 Plugin Metadata Endpoint
GET /-/<namespace>/<plugin-name>/meta.json
Returns metadata about the plugin, including all available versions and tags.
Response (200 OK):
{
"name": "@autonomous-lifecycle-protocol-alp/scrum",
"description": "Standard Scrum object types for ALP",
"author": "ALP Core Team",
"tags": {
"latest": "1.2.0",
"beta": "2.0.0-beta.1"
},
"versions": {
"1.0.0": {
"url": "https://registry.alp-protocol.org/@autonomous-lifecycle-protocol-alp/scrum/1.0.0/plugin.alp",
"integrity": "sha256:abc123def456...",
"dependencies": {}
},
"1.1.0": {
"url": "https://registry.alp-protocol.org/@autonomous-lifecycle-protocol-alp/scrum/1.1.0/plugin.alp",
"integrity": "sha256:fed789cba012...",
"dependencies": {}
},
"1.2.0": {
"url": "https://registry.alp-protocol.org/@autonomous-lifecycle-protocol-alp/scrum/1.2.0/plugin.alp",
"integrity": "sha256:111222333444...",
"dependencies": {
"@autonomous-lifecycle-protocol-alp/core-types": "^1.0.0"
}
}
}
}Rules:
tagsMUST map strings to valid semver versions present in theversionsobject.urlin the version object MAY be relative (e.g.,/download/@autonomous-lifecycle-protocol-alp/scrum/1.0.0/plugin.alp) or absolute.integrityis OPTIONAL but RECOMMENDED. If present, the parser MUST verify the downloaded file against this hash.
3.2 Plugin File Endpoint
GET <url_from_metadata>
Returns the raw .alp file for the requested version.
Response (200 OK):
Content-Type: text/plain; charset=utf-8(The raw ALP file content)
4. Parser Configuration (.alprc)
Projects or developers can configure registry behaviors using an .alprc (or .alprc.json) file located in the workspace root or the user's home directory.
Example .alprc:
{
"registries": {
"default": "https://registry.alp-protocol.org",
"@internal": "https://alp-registry.internal.company.com"
},
"auth": {
"https://alp-registry.internal.company.com": {
"token": "${ALP_INTERNAL_TOKEN}"
}
}
}4.1 Namespace Routing
If a plugin alias has a namespace (e.g., @internal/deploy), the parser checks if a specific registry URL is mapped to that namespace in .alprc. If a mapping exists, the parser uses that registry. Otherwise, it falls back to the default registry.
4.2 Authentication
Private registries require authentication. The .alprc file can provide a token (which may reference an environment variable). When communicating with an authenticated registry, the parser MUST include the token in the Authorization header:
Authorization: Bearer <token>Per-namespace tokens (registry hardening). A registry host MAY gate individual namespaces. A single global token protects every namespace; a namespace=token map protects only the listed namespaces (their reads and downloads require the matching bearer token), while unlisted namespaces remain public. A global token additionally protects the marketplace listing/search endpoint.
Publish-time auth. Publishing is a PUT /-/<namespace>/<plugin-name> request carrying the manifest and file contents inline. It MUST be gated by the target namespace's token; the server MUST reject publish requests that omit the token, present a wrong token, attempt path traversal outside the version directory, or declare a namespace that differs from the URL namespace. This prevents unauthenticated clients from injecting packages into any namespace.
4.3 Signature Trust Roots (.alprc trustedKeys)
Tokens prove who may publish; signatures (§4.2, v4.2) prove what was published was not tampered with. A consumer configures a trust root so installs are verified automatically without passing a key each time.
The .alprc trustedKeys field maps a namespace (@ns) or * (global) to a trust anchor, which is either:
- an inline PEM public key — the package signature's signer key MUST equal it exactly; or
- a fingerprint (
alp1…, the SHA-256 of the PEM public key) — the package signature's embedded signer key MUST hash to it.
{
"trustedKeys": {
"@demo": "alp1c0593b2f97ec8a92fa05e5bb",
"*": "-----BEGIN PUBLIC KEY-----\nMCow…\n-----END PUBLIC KEY-----"
}
}Note: on the command line, omit the leading
@when naming a namespace (alp keys trust add demo <fp>); commander interprets a leading@as a file-argument and would drop it. The config key is still written as@demo.
Rules:
- An
@nsentry takes precedence over*. - When a trust root is configured for a namespace, an install of a signed package MUST verify its signature against that root; a signature from an untrusted key MUST be rejected.
- When a trust root is configured for a namespace, an unsigned package for that namespace MUST be rejected.
- Packages in namespaces with no trust root install as before (signing remains optional and backward compatible).
- The CLI manages trust roots via
alp keys trust add <ns|*> <fingerprint|file>andalp keys trust list; the Python SDK resolves them throughRegistryClient.resolve_trust_entry/is_trusted, and audits signatures via the sharedverify_version_signaturehelper /RegistryClient.verify_remote. - Server-side enforcement (v4.4). A hosted registry (
alp serve --registry) whose.alprcdeclares a trust root for a namespace MUST rejectPUTuploads that are unsigned or signed by an untrusted key for that namespace. This closes the loop so a compromised token cannot publish untrusted packages into a protected namespace.alp registry verify <name>[@version]audits a stored version's signature against the local trust roots without installing.
5. Security & Verification
- HTTPS Required: All registry communication MUST occur over HTTPS. Parsers MUST reject plain HTTP connections.
- Strict Integrity: When resolving via a registry, if the metadata provides an
integrityhash, the parser MUST verify the downloaded file against it. If it fails, parsing MUST halt with a fatal error. - No Redirects for Metadata: Parsers SHOULD NOT follow HTTP redirects (301/302) when fetching
meta.jsonto prevent hijacking, unless explicitly configured to trust the redirect source.
6. Dependency Resolution Strategy
Plugins can depend on other plugins (see 11-plugins.md). The registry protocol relies on a Strict Singleton resolution strategy.
- The parser collects all direct plugin imports from the project.
- It fetches metadata for all imported plugins to discover their transitive dependencies.
- If two plugins depend on the same namespace/name, their version requirements are intersected.
- If the intersection is empty (e.g.,
^1.0.0and^2.0.0), the parser MUST produce a fatal Version Conflict Error. - Only ONE version of a plugin can exist in the final resolution graph.
This ensures that custom block markers (like @epic) have a single, unambiguous @type in the project.