You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Agentstration can import a Source Version from a direct URL or pasted YAML, and #159 established exact-digest verification for definitions and snapshots. It still lacks a first-class discovery layer for finding Sources across an official Agentstration catalogue, third-party catalogues, and private enterprise catalogues.
A single hard-coded catalogue would prevent local-first and enterprise deployments. Treating every registry, origin, publisher, or document under one undifferentiated "trusted" flag would also collapse distinct security questions:
Is this registry endpoint an origin the administrator trusts to provide discovery metadata?
Has the publisher identity been independently verified or designated Official?
Does the retrieved Source Version exactly match a known manifest digest?
When Channel content is materialized, does its immutable revision/content digest match separate verification evidence?
The hosting domain alone cannot answer all four questions. In particular, content served from an Agentstration-owned domain may be recognized as coming from a trusted origin, but must not automatically make a publisher Official or arbitrary bytes verified.
Expected outcome
Platform administrators can discover Sources through several locally configured registries. Agentstration ships with the official registry at https://registry.agentstration.io preconfigured, while allowing administrators to disable it, add external registries, or use an internal enterprise registry.
A registry is a bounded static publication composed of a lightweight versioned index and one or more bounded catalogue shards available over HTTP(S). The official deployment is initially published through GitHub Pages behind registry.agentstration.io, but the contract is host-neutral and can be served by any conforming HTTP server. Registry discovery metadata points to Source Version manifests and exact digests; the Source Providers declared by each Source Channel (Git, OCI, or future providers) remain solely responsible for acquiring Channel content.
The API and Console expose registry origin trust, publisher verification status, exact Source Version verification, and Channel Snapshot verification as separate states with clear evidence and provenance.
Proposed solution
Registry boundary
Introduce two explicitly distinct concepts:
an instance-owned local SourceRegistryRegistration Management resource for mutable administrator configuration;
a versioned static SourceRegistryIndex fetched from the configured endpoint and its referenced bounded SourceRegistry catalogue shards.
SourceRegistryRegistration is Platform-administered and initially supports only the instance scope. Its published configuration and observed state remain separate. Local configuration includes:
stable local UID, name, and display name;
catalogue URL and enabled/disabled state;
one explicit trust policy;
an optional credential reference for private enterprise endpoints;
refresh policy and cache settings.
Observed state includes the last attempt, last successful refresh, HTTP validators, exact catalogue digest, freshness/staleness, last-known-good cache identity, and actionable error. A credential is always a reference to an instance-scoped Secret owned by an instance-scoped Vault; credentials are never embedded in the registration or remote catalogue.
The fetched SourceRegistryIndex and each selected SourceRegistry shard are immutable inputs for one successful refresh and contain only discovery and verification metadata. It is not a package store, provider implementation, or execution surface. Removing a local registration never removes already imported Sources, materialized Snapshots, installed Packs, or immutable historical provenance.
The official registration has a stable well-known identity and is created only when absent. Product updates must never overwrite an administrator's enabled state, URL override, refresh settings, or other local choices.
Static index, catalogue shards, and executable v1 contract
The following contract is normative for registry validate, registry build, runtime ingestion, and the official publication. The public official registration URL is https://registry.agentstration.io/v1/index.json; the files remain host-neutral. A future incompatible Registry document contract is published beside it under another versioned path such as /v2/index.json, so older Agentstration releases keep consuming /v1/ without content negotiation or a Git-specific URL.
SourceRegistryIndex and release-line selection
SourceRegistryIndex is the small entry document fetched by a registry client. Its complete v1 shape is:
metadata.name is required and metadata.displayName is optional under the same identifier/string rules as SourceRegistry. definition.catalogs is required and non-empty. Every catalogue entry requires a unique name, a required compatibility.agentstration.minVersion, an optional maxVersionExclusive, a registryUrl, and an exact registryDigest. Bounds use the Semantic Version interval contract already implemented by #158/#191: minimum inclusive, maximum exclusive, and a missing maximum unbounded. Empty, malformed, or reversed intervals are invalid. For pre-1.0 Agentstration releases, publishers normally partition by compatible minor line (0.2.x, 0.3.x) rather than treating all 0.x releases as compatible.
The client validates the index, selects every entry whose interval contains the running Agentstration version, and downloads only those shards. No match produces an explicit no_compatible_registry_catalog state and never falls back to an arbitrary catalogue. Overlapping intervals are allowed: all matching shards are merged with the conflict rules below. A catalogue-level interval is only a discovery partition and never replaces the authoritative per-Channel compatibility evaluation after Source Version import.
registryUrl follows the same relative/same-origin, no-query, no-fragment, descendant-only URL rules as manifestUrl. registryDigest is the exact canonical digest of the referenced SourceRegistry shard. Index entries are canonically sorted by name. After typed validation and sorting, the index uses the same RFC 8785 canonical JSON rules as a shard; its digest is named indexDigest. No timestamp or build-environment value is injected.
An index is limited to 1 MiB, depth 16, and 128 catalogue entries. A client fetches the index plus only matching shards; it does not fetch Source Version manifests until detail/import and never materializes Channel content during registry discovery.
Shard membership is validated from referenced Source Version manifests. Every Source Version in a shard must declare at least one Channel whose Agentstration interval intersects the shard interval. A Source Version spanning several intervals may appear in several shards with the same publisher/name/version, manifest URL, and manifest digest. Equal observations are deduplicated without losing provenance; the same identity/version with different digests is a blocking conflict. Publishers may alternatively issue distinct Source Versions for different release lines when their definitions differ. Compatibility remains on Channels and the provider-specific Git ref remains inside Channel configuration.
SourceRegistry catalogue-shard schema
apiVersion is required and must equal agentstration.io/v1. kind is required and must equal SourceRegistry. Each selected catalogue shard uses kind: SourceRegistry. Its complete v1 shape is:
No other properties are permitted at any level in v1. Explicit null values are invalid; an optional value must be omitted. Publisher status is exactly one of Declared, Verified, Official, or Revoked; it is an assertion made by this registry and only becomes accepted evidence through the local registry trust policy.
metadata.name, publisher names, and Source names are 1-63 lowercase ASCII characters matching [a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?. version is opaque, compared ordinally, 1-128 Unicode scalar values, NFC-normalized, trimmed, and contains no control character. Display names are 1-256 characters, descriptions 1-4096, and URLs at most 2048. All strings must be valid Unicode and NFC-normalized. Publication time and fetch time are operational provenance, not fields of SourceRegistry v1: publishers may retain them in source control or HTTP metadata, and Agentstration records its own fetch/observation time locally.
Parsing, media types, and schema evolution
Registry inputs are UTF-8 JSON or single-document YAML. A UTF-8 BOM is accepted on input and never emitted. JSON duplicate properties are rejected. YAML uses the YAML 1.2 Core schema and rejects duplicate mapping keys, multiple documents, custom tags, anchors, aliases, merge keys, non-string mapping keys, and non-JSON values. YAML and JSON are converted to the same typed v1 model before validation or digest calculation.
For HTTP ingestion, accepted media types are application/json, application/yaml, application/x-yaml, and text/yaml; parameters are ignored except that an explicit charset must be UTF-8. A missing content type or application/octet-stream is accepted only when the final URL path ends in .json, .yaml, or .yml. Other media types are rejected. Local files use those same suffixes.
apiVersion selects the complete schema. A v1 reader rejects an unsupported version and every unknown field, including fields introduced by a future contract. The v1 shape is frozen; additions or semantic changes require a new apiVersion and an explicit reader/up-conversion path. Producers must not rely on consumers ignoring unknown data.
Canonical order and catalogue digest
Input array order is not significant. The normalized model sorts publishers by name, Sources by the tuple (publisher, name), and versions by opaque version, all with ordinal UTF-8 byte ordering; no semantic-version comparison is performed. registry validate accepts non-canonical input order, while registry build emits canonical order.
After validation and sorting, absent optional properties remain absent and the typed model is serialized as RFC 8785 JSON Canonicalization Scheme bytes, with no BOM and no trailing newline. The catalogue digest is sha256:<lowercase-hex> over those UTF-8 bytes and is named registryDigest. No timestamp or build-environment value is injected into the canonical model: identical logical inputs produce identical canonical bytes and the same digest. It is distinct from every version entry's manifestDigest, which continues to use the canonical Source Version algorithm from #159.
URL resolution and offline file mapping
manifestUrl is either a relative URI reference or an absolute HTTP(S) URL. In v1 it must have no user information, query string, or fragment. Cross-origin absolute URLs are not supported. An absolute URL must have the same normalized scheme, IDNA ASCII host, and effective port as the registry document. HTTP is accepted only when the local registration explicitly permits it.
Resolution follows RFC 3986 against the final registry document URL after redirects. The resolved URL must remain under the configured publication base path. Every path segment must be non-empty ASCII matching [A-Za-z0-9][A-Za-z0-9._-]{0,127}; ., .., backslashes, percent-encoding, encoded separators, control characters, repeated separators, drive/UNC forms, and a trailing separator are rejected. Redirect policy is evaluated separately for the registry fetch; a manifestUrl never authorizes an origin change.
For offline commands, --base-uri is an absolute URI ending in / and represents --publication-root; the public registry document is <base-uri>registry.json. Relative and same-origin absolute URLs are resolved exactly as at runtime, the base-path prefix is removed, and the remaining slash-separated path maps to a normalized descendant of --publication-root. Symlinks, junctions, reparse points, case-insensitive collisions, or any resolved escape fail validation.
Duplicates, conflicts, and latest
Within one registry, publisher names are unique; (publisher, source name) is unique; and (publisher, source name, version) is unique. A duplicate is invalid even when all values or digests are equal. Each Source references exactly one declared publisher and has at least one version. Each manifestDigest is exactly sha256: plus 64 lowercase hexadecimal characters. latest, when present, must equal exactly one declared opaque version using ordinal comparison. The validator also parses each referenced local Source Version and requires its publisher, Source name, version, and canonical digest to match the entry exactly.
Across registries, the merge rules defined below remain unchanged: equal portable identity/version/digest observations coexist with provenance; equal identity/version with different digests is a blocking conflict; latest is never merged globally.
Exact limits
Limits are applied before allocation where possible and fail closed:
registry document: 8 MiB after HTTP content decoding and before parsing;
referenced Source Version manifest: 1 MiB each;
parsed nesting depth: 16;
publishers: 256;
Sources: 1,000 per publisher and 5,000 total;
versions: 128 per Source and 25,000 total;
publisher evidence is represented only by the v1 status assertion; no unbounded extension bag exists;
identifier, version, display-name, description, URL, and path-segment lengths are the maxima defined above.
registry validate and build commands for a shard or complete index
The command shape is unchanged and accepts either one SourceRegistry shard or a complete SourceRegistryIndex publication root:
For a complete publication, index.yaml, index.yml, or index.json is a direct child of input-root. Every registryUrl maps to a declared SourceRegistry source file under the same root, and every shard manifestUrl maps to an existing regular descendant Source Version file. A direct SourceRegistry input remains supported for focused shard validation/build and must be named registry.yaml, registry.yml, or registry.json. Input and output roots are distinct and neither may contain the other.
A successful index build creates a new output tree containing only:
<output-root>/
index.json # canonical SourceRegistryIndex
index.sha256 # ASCII indexDigest followed by LF
registry-<catalog>.json # canonical SourceRegistry shard
registry-<catalog>.sha256 # ASCII registryDigest followed by LF
sources/<publisher>/<source>/<version>/source.yaml
Referenced Source Version manifests are copied byte-for-byte to their URL-mapped paths and shared identical targets are emitted once. A destination referenced more than once must resolve to identical bytes and digest. Files not reachable from the index and its shards are not copied. Duplicate output paths, case collisions, symlinks/reparse points, special files, and pre-existing non-empty output directories are rejected. Hosting-specific files such as CNAME or .nojekyll remain deployment-stage concerns.
registry validate performs the same index/shard parsing, limits, canonical digest verification, compatibility-interval intersection, URL mapping, cross-shard duplicate/conflict checks, latest checks, and exact manifest verification without network access or filesystem mutation. A direct shard validation uses no inferred compatibility interval and therefore validates the shard contract and manifests but not index membership.
Digest and verification compatibility
manifestDigest uses exactly the canonical Source Version digest algorithm already implemented for #159. Registry ingestion must call the same canonical reader and must not introduce a second normalization or verification rule. The digest of the SourceRegistry catalogue itself is a separate value with a separately named canonicalization contract; it never substitutes for a Source Version manifest digest or Channel Snapshot digest.
Registry observations provide verification evidence through a common evidence-provider boundary shared with the existing VerifiedSourceIndex implementation. The existing exact matching rule remains authoritative: Source identity, opaque version, and canonical manifest digest must all match. A registry adds discovery, provenance, publisher policy, and multiple evidence observations; it does not weaken or duplicate #159.
Official registry and hosting
Publish the official static registry from the gbaudrit/agentstration-registry repository, initially with GitHub Pages and a custom DNS name at registry.agentstration.io with HTTPS. Keep the DNS name as the stable client contract so the hosting implementation can move later without changing configured clients.
The official registry is preconfigured locally and identified as the Agentstration official registry. It must remain optional and non-blocking: offline startup and use of already imported Sources continue when it is disabled or unavailable.
Trust model
Formalize separate concepts and evidence:
Registry/origin trust — the local registration uses exactly one of Untrusted, TrustedOrigin, or Authoritative.
Untrusted permits bounded discovery but its assertions are not accepted as verification evidence; imports require an explicit warning/confirmation path.
TrustedOrigin recognizes the endpoint as an approved metadata origin but does not verify a publisher, Source Version, or Channel Snapshot.
Authoritative accepts explicit evidence only for publisher identities covered by the local authority policy. It is not a global power to make arbitrary publishers Official.
Publisher status — Declared, Verified, Official, or Revoked. Official is a publisher/evidence status, never a generic registry trust level. Verified and Official require accepted evidence whose authority covers the exact publisher identity.
Channel Snapshot verification — separate evidence for the exact Channel name, immutable provider revision, and complete Snapshot digest. It is never implied by Source Version verification or a mutable branch.
The built-in official registry is authoritative by default only for the Agentstration publisher identities and exact-digest evidence explicitly covered by its policy. An exact, boundary-safe HTTPS *.agentstration.io match may be shown as the informational classification Agentstration-owned origin, but does not by itself change the registration trust policy or make a registry authoritative. The bare domain, lookalike/suffix-confusion hosts, malformed IDNs, and unrelated subdomains receive no implicit authority.
Revocation is evaluated from accepted authoritative evidence. It blocks new imports and new consumption by default while retaining historical resources and provenance. A revocation assertion from a non-authoritative registry remains a visible observation/warning and cannot silently override accepted evidence.
Multi-registry merge behavior is deterministic and evidence-preserving:
the same publisher/name/version and digest is one portable version with every registry observation retained;
the same publisher/name/version with different digests is an explicit blocking conflict, never a winner-selection opportunity;
latest is a convenience pointer scoped to each registry observation and is never merged into an implicit global latest;
no result silently inherits the strongest trust or publisher status from another observation;
compatibility metadata may support discovery filters but never replaces the authoritative compatibility declared by the imported Source Version Channels.
Trust decisions and their evidence must be explainable in API responses and the Console. Trust downgrade, removal, or revocation on refresh must be visible and auditable without rewriting historical provenance.
Use ETag and Last-Modified conditional requests when available and record the resulting validators.
Parse, validate, and persist a complete new catalogue atomically before replacing the last-known-good cache.
Keep the last-known-good catalogue usable when a refresh fails; expose freshness and failure state rather than presenting stale data as current.
Define timeouts, bounded retries/backoff, jitter, maximum redirect count, maximum document size, maximum publishers/sources/versions, and cache retention.
Distinguish disabled, never fetched, fresh, stale, refresh failed, invalid catalogue, policy denied, and revoked states.
Historical operations retain the evidence used at the time. Later registry refreshes may change current presentation but must not rewrite prior audit records.
API and authorization
Expose canonical Management/API operations, reused by the Console, to:
list/get registries and their local configuration/observed state;
add, update, enable/disable, and remove external or internal registries;
configure registry trust and credential references under Platform authorization;
refresh one registry and inspect refresh history/errors;
search/browse the merged discovery view with per-registry observations;
list publishers and evaluate their Declared/Verified/Official/Revoked status with evidence;
inspect exact Source Version and Channel Snapshot verification independently;
import a selected exact Source Version from one immutable cached registry observation; the server resolves that observation and verifies identity, opaque version, and expected digest rather than trusting an arbitrary client-supplied digest;
distinguish published catalogue data, local mutable configuration, cached observed state, and immutable audit provenance.
The official preconfigured SourceRegistryRegistration has a stable well-known identity. It is inserted only when absent. Local policy may disable or override its operational settings, and product updates must not silently overwrite administrator choices.
Console experience
Extend the Source administration experience from #162 with:
a Registry list showing official/external/internal type, enabled state, configured trust, freshness, last result, and errors;
add/edit/disable/remove flows for locally configured registries;
an official registry badge distinct from a trusted-origin badge;
discovery search/filter by Source, publisher, registry, compatibility, and verification state;
Source results grouped by portable identity, with conflicts and all contributing registries visible;
separate, explainable badges for registry trust, publisher status, Source Version digest verification, and Snapshot verification;
version history and latest as a convenience pointer without hiding the exact selected version/digest;
confirmation and clear warnings before importing unverified, stale, conflicted, or revoked entries according to policy;
localized en-US/fr-FR product text and responsive desktop/mobile states.
Security
Registry documents and referenced manifests are untrusted remote input even when their origin is trusted.
Require HTTPS by default; permit HTTP only through explicit scoped local policy for development/intranet scenarios.
Apply the shared outbound HTTP/SSRF protections tracked by [Feature]: Harden outbound AEP transport trust and prevent SSRF #180 while still permitting explicitly authorized internal enterprise hosts. Validate every redirect and every resolved address against the same policy, defend against DNS rebinding, and reject credentials embedded in URLs.
Bound downloads, parsed documents, nesting, strings, collection counts, redirects, time, and cache storage.
Reject unsafe schemes, malformed/ambiguous hostnames, path traversal, and origin changes not allowed by policy.
Never execute registry content, scripts, hooks, or provider code.
Do not log credentials, tokens, or sensitive catalogue data. Private registries reference only instance-scoped Secrets owned by instance-scoped Vaults through the Agentstration secret/credential boundary.
Fail closed for canonical digest mismatch, identity mismatch, inconsistent duplicate version/digest publication, or revoked evidence.
Protect registry and trust-policy mutations with Platform-level authorization and retain audit events.
Keep standard startup and tests offline and deterministic.
Scope
Add the instance-owned SourceRegistryRegistration local configuration plus separate observed cache, refresh history, and provenance concepts.
Define the versioned host-neutral SourceRegistryIndex plus bounded SourceRegistry catalogue-shard contracts.
Transporting or materializing Source Channel content from the registry. Git, OCI, and other Source Providers retain that responsibility.
Inferring a Source Provider from a registry URL or Source manifest URL.
Making the official registry a mandatory startup/runtime dependency.
Automatically importing every discovered Source or materializing every Channel.
Treating every *.agentstration.io document, publisher, or byte sequence as Official or verified.
A full marketplace with ratings, reviews, billing, moderation, or search service infrastructure.
Mandatory cryptographic signatures, transparency logs, or key-distribution infrastructure in the first increment; the model should remain extensible to them.
Semantic dependency resolution between Sources or Packs.
Source identity/import and catalogue foundations come from #152 and #156. Exact Source Version and Channel Snapshot verification is defined by #159 and implemented by #193; this FR extends the same canonical digest and evidence boundary rather than replacing it. The integrated administration UX extends #162. Periodic refresh reuses the scheduling and observed-state patterns from #157.
Outbound endpoint, redirect, resolved-address, DNS-rebinding, and private-host policy must reuse or explicitly coordinate with the security boundary tracked by #180.
The existing gbaudrit/agentstration-registry repository and its initial Bootstrap sample publication (gbaudrit/agentstration-registry#1) provide the seed content. Static GitHub Pages publication and the registry.agentstration.io custom-domain setup are delegated to the child FR gbaudrit/agentstration-registry#2.
Deterministic build-time validation for registry publishers is delegated to child FR #234. That child publishes a versioned local .NET tool which reuses the production SourceManifestReader; the official publication in agentstration-registry#2 pins an exact tool version instead of implementing another manifest canonicalization.
This registry feature is not required for direct URL/YAML Source import or local Bootstrap/Pack workflows from #150.
Alternatives considered
One hard-coded central marketplace: rejected because it prevents enterprise/internal registries and makes Agentstration dependent on an online service.
Use raw.githubusercontent.com directly as the permanent client URL: rejected because registry.agentstration.io provides a stable contract independent of the initial host.
Serve packages directly from the registry: rejected because it duplicates Source Provider transport and couples discovery to GitHub Pages.
Trust by hostname alone: rejected because origin control does not prove publisher identity or exact content integrity.
Flatten all trust into one boolean: rejected because registry origin, publisher identity, definition digest, and Channel content are independent claims.
Only retain latest: rejected because it weakens auditability and reproducibility; latest is a convenience pointer, not an immutable identity.
Deployment impact
Configuration change
The product gains a preconfigured official registry and optional outbound HTTPS refresh. Registry failure must be non-fatal and must not make startup network-dependent. The official publishing pipeline gains GitHub Pages/custom-domain/DNS/TLS configuration, while the catalogue contract remains portable to another static HTTP host.
Acceptance criteria
A versioned, bounded SourceRegistryIndex and SourceRegistry shard contract is documented with canonical index/catalogue digests, compatibility selection, and URL-resolution rules.
The executable v1 schema, strict unknown-field behavior, canonical ordering, RFC 8785 catalogue digest, media rules, exact limits, duplicate/latest rules, and same-origin URL mapping are shared by runtime ingestion, registry validate, and registry build.
registry build accepts either a focused shard or the complete index publication layout and emits only canonical index/shards, their digest files, and byte-for-byte referenced Source Version manifests.
Local mutable configuration is represented by an instance-owned SourceRegistryRegistration, distinct from the fetched static document and its observed cache.
The official catalogue is published from gbaudrit/agentstration-registry through GitHub Pages with HTTPS and the custom domain by gbaudrit/agentstration-registry#2.
Administrators can configure several enabled external or internal registries with independent trust and refresh settings.
Administrator overrides survive product upgrades.
Registry/origin trust, publisher status, exact Source Version verification, and exact Snapshot verification are distinct API/domain/UI states.
Publisher states include well-defined equivalents of Declared, Verified, Official, and Revoked with explicit transition/evidence semantics.
An exact boundary-safe *.agentstration.io HTTPS match is only an informational Agentstration-owned-origin classification; only the well-known official registration is authoritative by default, and hostname alone never makes publisher or content Official/verified.
Lookalike domains, suffix confusion, malformed hosts, and unauthorized redirects do not match the Agentstration-owned-origin policy.
A Source Version is verified only when identity and canonical exact manifest digest match accepted evidence.
A verified Source Version does not automatically verify mutable Channel content.
Registry results from several origins retain all observations, conflicts, and evidence instead of silently collapsing trust.
Equal identity/version/digest observations merge without losing provenance; equal identity/version with different digests is a blocking conflict; latest remains registry-local.
Catalogue versions and optional latest pointers remain explicit and shard-local; selection/import pins an exact version and expected digest.
An Agentstration installation downloads only the index and shards whose declared interval contains its running version; manifests remain on-demand.
Shard membership is validated from existing per-Channel compatibility intervals and never becomes a second compatibility authority.
The same Source Version may occur in multiple compatible shards only with the same identity, URL, and digest; conflicts fail closed.
Exact import resolves a retained registry observation server-side and cannot manufacture verification from an arbitrary client-supplied digest.
Registry refresh never imports all Sources or materializes Channels implicitly.
Conditional refresh, bounded retries/backoff, atomic validation, and last-known-good cache behavior are implemented.
Offline/unavailable registries do not prevent startup, direct Source import, or use of already imported Sources.
Staleness, refresh failures, invalid catalogues, policy denials, conflicts, and revocations are actionable in API and Console.
Bootstrap and Pack consumption originating from a registry observation retains that observation in operation provenance and still revalidates the exact Source Version, Channel compatibility, and immutable Snapshot; registry shard membership or latest never authorizes consumption by itself.
Provenance records the registry observation and trust evidence used for each exact Source import without rewriting historical operations.
Disabling or removing a registration does not remove imported Sources, Snapshots, installed Packs, or their historical evidence.
Platform authorization protects registry, credential-reference, and trust-policy mutations.
Private enterprise registries can reference an instance-scoped Secret in an instance-scoped Vault without publishing or logging credentials.
Console discovery and registry administration reuse the canonical application/API services and are localized in en-US/fr-FR.
Documentation clearly distinguishes discovery registry, Source Version manifest retrieval, and Source Provider content transport.
Tests and validation
Contract tests cover index and shard YAML/JSON parsing, schema evolution, canonicalization, exact digest calculation, compatibility-interval selection/intersection, relative/absolute URL resolution, opaque versions, shard-local latest pointers, cross-shard duplicates/conflicts, and limits.
Trust-policy tests cover every registry level, publisher status, revocation, trust downgrade, conflicting registries, and evidence explanation.
Host-policy tests cover the bare domain, valid subdomains, lookalikes, suffix confusion, IDN/punycode, ports, redirects, DNS changes/rebinding defenses, HTTP opt-in, and explicitly authorized private hosts.
Refresh tests cover ETag/Last-Modified, 304, timeout, retry/backoff, invalid replacement, atomic cache swap, stale last-known-good data, and recovery.
API authorization and audit tests cover every registry/trust mutation and exact-version import.
Console tests cover official/external/internal registries, discovery merge/conflicts, all trust badges, freshness/errors, exact version selection, warnings, and desktop/mobile localization.
Integration fixtures use local deterministic HTTP servers; startup and standard test suites require no public network.
Documentation includes the static hosting contract, GitHub Pages/custom DNS setup, enterprise registry example, trust model, cache/refresh behavior, failure recovery, API, Console, and the non-transport boundary.
This FR intentionally makes the registry a discovery and trust-metadata layer. A Source Version continues to declare Channels, and each locally bound Source Provider remains responsible for resolving and materializing the exact Channel revision.
Preliminary checks
I searched for an existing issue covering this request.
Area
Cross-cutting
Feature type
Integration
Problem or need
Agentstration can import a Source Version from a direct URL or pasted YAML, and #159 established exact-digest verification for definitions and snapshots. It still lacks a first-class discovery layer for finding Sources across an official Agentstration catalogue, third-party catalogues, and private enterprise catalogues.
A single hard-coded catalogue would prevent local-first and enterprise deployments. Treating every registry, origin, publisher, or document under one undifferentiated "trusted" flag would also collapse distinct security questions:
The hosting domain alone cannot answer all four questions. In particular, content served from an Agentstration-owned domain may be recognized as coming from a trusted origin, but must not automatically make a publisher Official or arbitrary bytes verified.
Expected outcome
Platform administrators can discover Sources through several locally configured registries. Agentstration ships with the official registry at https://registry.agentstration.io preconfigured, while allowing administrators to disable it, add external registries, or use an internal enterprise registry.
A registry is a bounded static publication composed of a lightweight versioned index and one or more bounded catalogue shards available over HTTP(S). The official deployment is initially published through GitHub Pages behind registry.agentstration.io, but the contract is host-neutral and can be served by any conforming HTTP server. Registry discovery metadata points to Source Version manifests and exact digests; the Source Providers declared by each Source Channel (Git, OCI, or future providers) remain solely responsible for acquiring Channel content.
The API and Console expose registry origin trust, publisher verification status, exact Source Version verification, and Channel Snapshot verification as separate states with clear evidence and provenance.
Proposed solution
Registry boundary
Introduce two explicitly distinct concepts:
SourceRegistryRegistrationManagement resource for mutable administrator configuration;SourceRegistryIndexfetched from the configured endpoint and its referenced boundedSourceRegistrycatalogue shards.SourceRegistryRegistrationis Platform-administered and initially supports only the instance scope. Its published configuration and observed state remain separate. Local configuration includes:Observed state includes the last attempt, last successful refresh, HTTP validators, exact catalogue digest, freshness/staleness, last-known-good cache identity, and actionable error. A credential is always a reference to an instance-scoped Secret owned by an instance-scoped Vault; credentials are never embedded in the registration or remote catalogue.
The fetched
SourceRegistryIndexand each selectedSourceRegistryshard are immutable inputs for one successful refresh and contain only discovery and verification metadata. It is not a package store, provider implementation, or execution surface. Removing a local registration never removes already imported Sources, materialized Snapshots, installed Packs, or immutable historical provenance.The official registration has a stable well-known identity and is created only when absent. Product updates must never overwrite an administrator's enabled state, URL override, refresh settings, or other local choices.
Static index, catalogue shards, and executable v1 contract
The following contract is normative for
registry validate,registry build, runtime ingestion, and the official publication. The public official registration URL ishttps://registry.agentstration.io/v1/index.json; the files remain host-neutral. A future incompatible Registry document contract is published beside it under another versioned path such as/v2/index.json, so older Agentstration releases keep consuming/v1/without content negotiation or a Git-specific URL.SourceRegistryIndex and release-line selection
SourceRegistryIndexis the small entry document fetched by a registry client. Its complete v1 shape is:metadata.nameis required andmetadata.displayNameis optional under the same identifier/string rules asSourceRegistry.definition.catalogsis required and non-empty. Every catalogue entry requires a uniquename, a requiredcompatibility.agentstration.minVersion, an optionalmaxVersionExclusive, aregistryUrl, and an exactregistryDigest. Bounds use the Semantic Version interval contract already implemented by #158/#191: minimum inclusive, maximum exclusive, and a missing maximum unbounded. Empty, malformed, or reversed intervals are invalid. For pre-1.0 Agentstration releases, publishers normally partition by compatible minor line (0.2.x,0.3.x) rather than treating all0.xreleases as compatible.The client validates the index, selects every entry whose interval contains the running Agentstration version, and downloads only those shards. No match produces an explicit
no_compatible_registry_catalogstate and never falls back to an arbitrary catalogue. Overlapping intervals are allowed: all matching shards are merged with the conflict rules below. A catalogue-level interval is only a discovery partition and never replaces the authoritative per-Channel compatibility evaluation after Source Version import.registryUrlfollows the same relative/same-origin, no-query, no-fragment, descendant-only URL rules asmanifestUrl.registryDigestis the exact canonical digest of the referencedSourceRegistryshard. Index entries are canonically sorted byname. After typed validation and sorting, the index uses the same RFC 8785 canonical JSON rules as a shard; its digest is namedindexDigest. No timestamp or build-environment value is injected.An index is limited to 1 MiB, depth 16, and 128 catalogue entries. A client fetches the index plus only matching shards; it does not fetch Source Version manifests until detail/import and never materializes Channel content during registry discovery.
Shard membership is validated from referenced Source Version manifests. Every Source Version in a shard must declare at least one Channel whose Agentstration interval intersects the shard interval. A Source Version spanning several intervals may appear in several shards with the same publisher/name/version, manifest URL, and manifest digest. Equal observations are deduplicated without losing provenance; the same identity/version with different digests is a blocking conflict. Publishers may alternatively issue distinct Source Versions for different release lines when their definitions differ. Compatibility remains on Channels and the provider-specific Git
refremains inside Channel configuration.SourceRegistry catalogue-shard schema
apiVersionis required and must equalagentstration.io/v1.kindis required and must equalSourceRegistry. Each selected catalogue shard useskind: SourceRegistry. Its complete v1 shape is:No other properties are permitted at any level in v1. Explicit
nullvalues are invalid; an optional value must be omitted. Publisherstatusis exactly one ofDeclared,Verified,Official, orRevoked; it is an assertion made by this registry and only becomes accepted evidence through the local registry trust policy.metadata.name, publisher names, and Source names are 1-63 lowercase ASCII characters matching[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.versionis opaque, compared ordinally, 1-128 Unicode scalar values, NFC-normalized, trimmed, and contains no control character. Display names are 1-256 characters, descriptions 1-4096, and URLs at most 2048. All strings must be valid Unicode and NFC-normalized. Publication time and fetch time are operational provenance, not fields ofSourceRegistryv1: publishers may retain them in source control or HTTP metadata, and Agentstration records its own fetch/observation time locally.Parsing, media types, and schema evolution
Registry inputs are UTF-8 JSON or single-document YAML. A UTF-8 BOM is accepted on input and never emitted. JSON duplicate properties are rejected. YAML uses the YAML 1.2 Core schema and rejects duplicate mapping keys, multiple documents, custom tags, anchors, aliases, merge keys, non-string mapping keys, and non-JSON values. YAML and JSON are converted to the same typed v1 model before validation or digest calculation.
For HTTP ingestion, accepted media types are
application/json,application/yaml,application/x-yaml, andtext/yaml; parameters are ignored except that an explicit charset must be UTF-8. A missing content type orapplication/octet-streamis accepted only when the final URL path ends in.json,.yaml, or.yml. Other media types are rejected. Local files use those same suffixes.apiVersionselects the complete schema. A v1 reader rejects an unsupported version and every unknown field, including fields introduced by a future contract. The v1 shape is frozen; additions or semantic changes require a newapiVersionand an explicit reader/up-conversion path. Producers must not rely on consumers ignoring unknown data.Canonical order and catalogue digest
Input array order is not significant. The normalized model sorts publishers by
name, Sources by the tuple(publisher, name), and versions by opaqueversion, all with ordinal UTF-8 byte ordering; no semantic-version comparison is performed.registry validateaccepts non-canonical input order, whileregistry buildemits canonical order.After validation and sorting, absent optional properties remain absent and the typed model is serialized as RFC 8785 JSON Canonicalization Scheme bytes, with no BOM and no trailing newline. The catalogue digest is
sha256:<lowercase-hex>over those UTF-8 bytes and is namedregistryDigest. No timestamp or build-environment value is injected into the canonical model: identical logical inputs produce identical canonical bytes and the same digest. It is distinct from every version entry'smanifestDigest, which continues to use the canonical Source Version algorithm from #159.URL resolution and offline file mapping
manifestUrlis either a relative URI reference or an absolute HTTP(S) URL. In v1 it must have no user information, query string, or fragment. Cross-origin absolute URLs are not supported. An absolute URL must have the same normalized scheme, IDNA ASCII host, and effective port as the registry document. HTTP is accepted only when the local registration explicitly permits it.Resolution follows RFC 3986 against the final registry document URL after redirects. The resolved URL must remain under the configured publication base path. Every path segment must be non-empty ASCII matching
[A-Za-z0-9][A-Za-z0-9._-]{0,127};.,.., backslashes, percent-encoding, encoded separators, control characters, repeated separators, drive/UNC forms, and a trailing separator are rejected. Redirect policy is evaluated separately for the registry fetch; amanifestUrlnever authorizes an origin change.For offline commands,
--base-uriis an absolute URI ending in/and represents--publication-root; the public registry document is<base-uri>registry.json. Relative and same-origin absolute URLs are resolved exactly as at runtime, the base-path prefix is removed, and the remaining slash-separated path maps to a normalized descendant of--publication-root. Symlinks, junctions, reparse points, case-insensitive collisions, or any resolved escape fail validation.Duplicates, conflicts, and latest
Within one registry, publisher names are unique;
(publisher, source name)is unique; and(publisher, source name, version)is unique. A duplicate is invalid even when all values or digests are equal. Each Source references exactly one declared publisher and has at least one version. EachmanifestDigestis exactlysha256:plus 64 lowercase hexadecimal characters.latest, when present, must equal exactly one declared opaque version using ordinal comparison. The validator also parses each referenced local Source Version and requires its publisher, Source name, version, and canonical digest to match the entry exactly.Across registries, the merge rules defined below remain unchanged: equal portable identity/version/digest observations coexist with provenance; equal identity/version with different digests is a blocking conflict;
latestis never merged globally.Exact limits
Limits are applied before allocation where possible and fail closed:
registry validate and build commands for a shard or complete index
The command shape is unchanged and accepts either one
SourceRegistryshard or a completeSourceRegistryIndexpublication root:For a complete publication,
index.yaml,index.yml, orindex.jsonis a direct child ofinput-root. EveryregistryUrlmaps to a declaredSourceRegistrysource file under the same root, and every shardmanifestUrlmaps to an existing regular descendant Source Version file. A directSourceRegistryinput remains supported for focused shard validation/build and must be namedregistry.yaml,registry.yml, orregistry.json. Input and output roots are distinct and neither may contain the other.A successful index build creates a new output tree containing only:
Referenced Source Version manifests are copied byte-for-byte to their URL-mapped paths and shared identical targets are emitted once. A destination referenced more than once must resolve to identical bytes and digest. Files not reachable from the index and its shards are not copied. Duplicate output paths, case collisions, symlinks/reparse points, special files, and pre-existing non-empty output directories are rejected. Hosting-specific files such as
CNAMEor.nojekyllremain deployment-stage concerns.registry validateperforms the same index/shard parsing, limits, canonical digest verification, compatibility-interval intersection, URL mapping, cross-shard duplicate/conflict checks,latestchecks, and exact manifest verification without network access or filesystem mutation. A direct shard validation uses no inferred compatibility interval and therefore validates the shard contract and manifests but not index membership.Digest and verification compatibility
manifestDigestuses exactly the canonical Source Version digest algorithm already implemented for #159. Registry ingestion must call the same canonical reader and must not introduce a second normalization or verification rule. The digest of theSourceRegistrycatalogue itself is a separate value with a separately named canonicalization contract; it never substitutes for a Source Version manifest digest or Channel Snapshot digest.Registry observations provide verification evidence through a common evidence-provider boundary shared with the existing
VerifiedSourceIndeximplementation. The existing exact matching rule remains authoritative: Source identity, opaque version, and canonical manifest digest must all match. A registry adds discovery, provenance, publisher policy, and multiple evidence observations; it does not weaken or duplicate #159.Official registry and hosting
Publish the official static registry from the gbaudrit/agentstration-registry repository, initially with GitHub Pages and a custom DNS name at registry.agentstration.io with HTTPS. Keep the DNS name as the stable client contract so the hosting implementation can move later without changing configured clients.
The official registry is preconfigured locally and identified as the Agentstration official registry. It must remain optional and non-blocking: offline startup and use of already imported Sources continue when it is disabled or unavailable.
Trust model
Formalize separate concepts and evidence:
Untrusted,TrustedOrigin, orAuthoritative.Untrustedpermits bounded discovery but its assertions are not accepted as verification evidence; imports require an explicit warning/confirmation path.TrustedOriginrecognizes the endpoint as an approved metadata origin but does not verify a publisher, Source Version, or Channel Snapshot.Authoritativeaccepts explicit evidence only for publisher identities covered by the local authority policy. It is not a global power to make arbitrary publishers Official.Declared,Verified,Official, orRevoked.Officialis a publisher/evidence status, never a generic registry trust level.VerifiedandOfficialrequire accepted evidence whose authority covers the exact publisher identity.The built-in official registry is authoritative by default only for the Agentstration publisher identities and exact-digest evidence explicitly covered by its policy. An exact, boundary-safe HTTPS
*.agentstration.iomatch may be shown as the informational classification Agentstration-owned origin, but does not by itself change the registration trust policy or make a registry authoritative. The bare domain, lookalike/suffix-confusion hosts, malformed IDNs, and unrelated subdomains receive no implicit authority.Revocation is evaluated from accepted authoritative evidence. It blocks new imports and new consumption by default while retaining historical resources and provenance. A revocation assertion from a non-authoritative registry remains a visible observation/warning and cannot silently override accepted evidence.
Multi-registry merge behavior is deterministic and evidence-preserving:
latestis a convenience pointer scoped to each registry observation and is never merged into an implicit global latest;Trust decisions and their evidence must be explainable in API responses and the Console. Trust downgrade, removal, or revocation on refresh must be visible and auditable without rewriting historical provenance.
Refresh, cache, and failure semantics
Provenance and audit
For each discovery/import decision retain enough information to identify:
Historical operations retain the evidence used at the time. Later registry refreshes may change current presentation but must not rewrite prior audit records.
API and authorization
Expose canonical Management/API operations, reused by the Console, to:
The official preconfigured
SourceRegistryRegistrationhas a stable well-known identity. It is inserted only when absent. Local policy may disable or override its operational settings, and product updates must not silently overwrite administrator choices.Console experience
Extend the Source administration experience from #162 with:
Security
Registry documents and referenced manifests are untrusted remote input even when their origin is trusted.
Scope
SourceRegistryRegistrationlocal configuration plus separate observed cache, refresh history, and provenance concepts.SourceRegistryIndexplus boundedSourceRegistrycatalogue-shard contracts.Out of scope
Dependencies
Parent feature: #150.
Source identity/import and catalogue foundations come from #152 and #156. Exact Source Version and Channel Snapshot verification is defined by #159 and implemented by #193; this FR extends the same canonical digest and evidence boundary rather than replacing it. The integrated administration UX extends #162. Periodic refresh reuses the scheduling and observed-state patterns from #157.
Outbound endpoint, redirect, resolved-address, DNS-rebinding, and private-host policy must reuse or explicitly coordinate with the security boundary tracked by #180.
The existing
gbaudrit/agentstration-registryrepository and its initial Bootstrap sample publication (gbaudrit/agentstration-registry#1) provide the seed content. Static GitHub Pages publication and theregistry.agentstration.iocustom-domain setup are delegated to the child FR gbaudrit/agentstration-registry#2.Deterministic build-time validation for registry publishers is delegated to child FR #234. That child publishes a versioned local .NET tool which reuses the production
SourceManifestReader; the official publication inagentstration-registry#2pins an exact tool version instead of implementing another manifest canonicalization.This registry feature is not required for direct URL/YAML Source import or local Bootstrap/Pack workflows from #150.
Alternatives considered
Deployment impact
Configuration change
The product gains a preconfigured official registry and optional outbound HTTPS refresh. Registry failure must be non-fatal and must not make startup network-dependent. The official publishing pipeline gains GitHub Pages/custom-domain/DNS/TLS configuration, while the catalogue contract remains portable to another static HTTP host.
Acceptance criteria
registry validate, andregistry build.registry buildaccepts either a focused shard or the complete index publication layout and emits only canonical index/shards, their digest files, and byte-for-byte referenced Source Version manifests.SourceRegistryRegistration, distinct from the fetched static document and its observed cache.manifestDigestreuses the exact canonical digest implementation from Publish and recognize verified Source definitions #159; the registry introduces no second Source verification algorithm.gbaudrit/agentstration-registrythrough GitHub Pages with HTTPS and the custom domain by gbaudrit/agentstration-registry#2.*.agentstration.ioHTTPS match is only an informational Agentstration-owned-origin classification; only the well-known official registration is authoritative by default, and hostname alone never makes publisher or content Official/verified.latestremains registry-local.Tests and validation
Additional context
Parent feature: #150. Related completed trust feature: #159 / #193. Related Console feature: #162. Outbound transport security: #180. Initial official-content repository: https://github.com/gbaudrit/agentstration-registry. Static GitHub Pages publication child: gbaudrit/agentstration-registry#2.
This FR intentionally makes the registry a discovery and trust-metadata layer. A Source Version continues to declare Channels, and each locally bound Source Provider remains responsible for resolving and materializing the exact Channel revision.
Preliminary checks