OAS v0.21.0
OAS v0.21.0
OAS v0.21.0 changes where the official package catalog comes from. The catalog
is now read from the OAS repository at resolution time instead of only from the
copy bundled inside the installed kernel.
The catalog is read from the OAS repo
The source of truth is package-catalog.json on main in
OAS-Framework/oas. A catalog-consuming
command fetches it, validates its shape, and caches it.
Why. The catalog is an identity map — official short id → repository, ref
and package root. Before this release it could only change by publishing a new
kernel, so a newly published official package (or a corrected entry) reached
deployments a release late, and the strict "release the kernel, then the
packages, then a catalog patch" ordering was a recurring tax. Reading the
catalog from main decouples publishing a package from cutting a kernel.
Resolution order.
OAS_PACKAGE_CATALOG— an explicit local file. It replaces the catalog
entirely: no fetch, no cache write, unchanged failure behavior for a broken
file. This is the hermetic escape hatch for tests and pinned environments.- The remote catalog, with a short bounded timeout, validated before use and
cached on success. - The cached last successful copy — the normal source for every read that did
not just fetch, and the fallback when a fetch fails or the payload is
invalid. When a command attempted a refresh and fell back, one line goes to
stderr naming the cache age. - The
package-catalog.jsonbundled in the npm package, as the last-resort
seed for a host that has never reached the network.
The bundled catalog stays in the published package. It is the seed, not the
source of truth.
Remote payloads are untrusted input. A catalog entry's url, ref and
path end up in a git clone argv and on the filesystem, so a remote payload —
and the cache, which only ever holds remote-derived content — is validated entry
by entry: a valid package id, an https://-only url (no file:, ssh:,
git: or scp-like spellings), a plain option-free ref, a canonical relative
path, and unknown keys dropped. One bad entry disqualifies the payload, which
becomes a fetch failure. Redirects are followed only to https GitHub hosts,
and the body cap is enforced in bytes. An OAS_PACKAGE_CATALOG override file
is exempt from these tightenings: it is operator-controlled and local, and
keeps the pre-release semantics exactly, file:// urls included.
Reproducibility is unchanged
Locks pin exact commits. The catalog supplies identity and discovery only: it
advances no lock, changes no installed bytes, and grants no executable trust.
The same oas-lock.json restores the same commits whether the catalog came from
the network, the cache, or the bundle.
Restores stay offline. A bare oas install restore of an existing lock — and
the recursive workspace reconciliation — never attempts a catalog fetch, because
the lock already names what to restore. Only new work resolves the catalog:
oas install <catalog-id>, oas update <package>, oas migrate, and the
oas init forms that resolve a fundamental layer or --package. An install
whose source names its own transport (./dir, git:host/org/repo,
https://…) is not a catalog lookup and performs no fetch, and oas doctor
never fetches — diagnostics stay off the network.
Cache and offline behavior
The cache lives at ~/.oas/package-catalog.cache.json (under $OAS_HOME_DIR
when set). It stores the payload with its fetch timestamp and is replaced
atomically: an exclusively created temp file with an unguessable random name,
0600 always, fsynced, then renamed — so a name planted in advance cannot be
followed. The containing directory is created 0700 when the kernel creates it;
a directory that already exists keeps the permissions the host gave it. A cache
that is missing, corrupt, or fails entry validation is treated as absent, never
as a failure, and a cache that cannot be written never fails the command (it is
reported as catalog.cacheError in oas doctor --json).
Offline hosts keep working: the cache answers, with a single stderr line such as
oas: official package catalog unreachable (timed out after 4000ms) — using the cached copy from 26h ago (~/.oas/package-catalog.cache.json)
--json output is unaffected: warnings never enter the envelope on stdout.
Set OAS_CATALOG_FETCH=off on an air-gapped host to switch the remote attempt
off entirely and resolve straight to the cache and then the seed. Its messages
say the fetch was disabled — they never claim the remote was unreachable,
because nothing was contacted.
Doctor reports catalog provenance — without fetching
oas doctor names the catalog source this host is actually serving —
override, remote, cache or bundled — with the URL or file path, the
cache location, and the cache age when a cached copy is being served. The same
data is available structurally as catalog in oas doctor --json, which also
carries refreshedThisRun, refreshedBy, fetchDisabled and cacheError.
Doctor performs no fetch: diagnostics stay off the network, so a broken
network cannot make the diagnostic hang, and doctor never changes the state it
is diagnosing. Its lines therefore never claim the remote "was unreachable" —
they report what is stored and name the acquiring commands that do refresh it.
A broken OAS_PACKAGE_CATALOG override is reported as broken rather than
rendered as a healthy override.
Desktop compatibility
Desktop API remains version 1: the surface Desktop probes and drives
(version --json, spawn --json, okf harvest --json) is unchanged by this
release, and the desktop server reads no catalog of its own — it only classifies
catalog: lock sources as strings, which this change does not touch.
The accepted CLI band in packages/desktop/cli-locator.mjs is widened to
>=0.18.0 <0.22.0 so it admits the kernel published by this same tag (Desktop
and the kernel publish in lockstep, and a band excluding its own kernel would
ship an app degraded to observation-only). DESKTOP_API stays 1; only the
upper bound moves, and the band-edge tests pin the new edges.
Security posture
The fetched URL is a constant compiled into the kernel. It is never taken from
configuration, a lock, the catalog itself, or a command-line argument, so no
input can turn the kernel into a fetch proxy for another address. The only
override, OAS_PACKAGE_CATALOG, names a local file exactly as before.
See Distribution packages for the full contract and
the package engine contract for the
provenance and fetch-classification tables.