Plugin Policies
Use on: plugin policies to require your company's OCI registry and approve specific plugin
packages. Atmos checks destinations before remote access and checks statically inspected packages
before installation or execution, including reuse from the shared cache.
Discovery policies apply to both optional catalog snapshots and direct OCI repository indexes. Atmos checks configured sources and result destinations even when search is served entirely from the local cache. Cached metadata never grants installation trust; current package policy, signature verification, compatibility, and integrity checks still apply.
Restrict registries and packages
This policy permits only registry.example.com and the plugin packages acme/oidc and acme/platform.
It denies local development packages and explicit offline layout imports:
apiVersion: atmos/v1
kind: Policy
metadata:
name: company-plugins
description: Require approved company plugin registries and packages.
spec:
policies:
approved-registries:
on: plugin
action: deny
reason: Plugins and catalogs must use the company registry
with:
allowed_registries:
- registry.example.com
rule: >-
input.origin in ['development', 'layout'] || !(input.registry in params.allowed_registries)
approved-packages:
on: plugin
action: deny
reason: Only approved plugin packages may be installed or activated
with:
allowed_plugins:
- acme/oidc
- acme/platform
rule: "input.phase == 'package' && !(input.name in params.allowed_plugins)"
Registry entries are exact authorities, including a port when applicable, without oci:// or a
repository path. Package entries are exact metadata.name values, independent of the project's
plugin alias or the OCI repository name. These examples do not use wildcards.
The approved registries and
approved packages library entries include reusable
manifests, CEL and Starlark implementations, and shared test cases. Install one implementation of
each policy. To use Starlark in the combined example, set engine: starlark and replace the
respective rule bodies:
return ctx.input["origin"] in ["development", "layout"] or ctx.input["registry"] not in ctx.params["allowed_registries"]
return ctx.input["phase"] == "package" and ctx.input["name"] not in ctx.params["allowed_plugins"]
When checks run
Atmos evaluates plugin rules at two phases:
| Phase | Available evidence | Purpose |
|---|---|---|
source | Registry, repository, normalized source, operation, origin, development flag, local path, active profiles | Reject a destination or import origin before registry access or cached reuse |
package | Source fields plus the statically inspected manifest and release digest when available | Reject a package before installation, activation, testing, publication, or signing |
Source checks do not know the package's manifest name. A name restriction therefore selects
phase == 'package'. Registry restrictions apply in both phases and should not depend on package
metadata.
Discovery catalogs and the package repositories they reference are checked separately. An approved
catalog cannot make an unapproved package registry acceptable. Automatic installation and activation
of cached packages use the same policies as explicit installation; a previously installed package
must satisfy the current policy. Cached bytes are selected through the project lockfile and marked
origin: cache; protect that lockfile as part of the trusted workflow.
operation identifies the guarded action:
| Operation | Action |
|---|---|
install | Install a release or restore a declared package |
activate | Load a plugin for use, including a cached package or a local path package |
test | Run plugin conformance tests (atmos plugin test) on a local, unsigned plugin directory |
search | Check configured discovery sources, including when results come from the local cache |
publish | Publish a plugin release |
sign | Sign a plugin release |
registry.read | Read a discovery catalog or registry metadata |
registry.write | Write a discovery catalog |
registry.delete | Delete registry content |
registry.configure | Configure a catalog reference |
registry.login | Store credentials for a registry |
atmos plugin test is governed only by on: plugin rules for the test operation. It is not subject to
plugins.policy.require_signature, because an author must be able to test a plugin before it can be
signed. atmos plugin build compiles local source, admits nothing, and runs no
plugin code, so it is not an operation and a registry-allowlist rule such as the example above does not
block it.
Registry operations have no inspected plugin package, so they use the source phase. Credentials,
tokens, and authentication secrets are never included in this input.
Plugin input
Every field below is present. Unknown strings are empty and unknown capabilities use an empty list.
| Field | Type | Meaning |
|---|---|---|
operation | String | Guarded action from the table above |
phase | String | source or package |
origin | String | registry for registry access, configuration, or login; cache for reuse through a project lock; development for local development or conformance testing; layout for explicit --from-layout imports |
source | String | Declared repository normalized as oci://registry/repository without a tag or digest; registry login uses oci://registry; local development uses an empty string |
registry | String | Registry authority, including an explicit port; empty for local development |
repository | String | Repository path within the registry; empty for registry login and local development |
name | String | Inspected manifest's metadata.name; empty in the source phase |
version | String | Inspected manifest's metadata.version; empty in the source phase |
digest | String | Release digest when available; empty when unknown |
runtime | String | Manifest runtime, such as native; empty in the source phase |
capabilities | Array of strings | Sorted capability IDs discovered by the extension registry, such as auth.identity, auth.provider, and command; empty in the source phase |
development | Boolean | Whether the request uses a local development package, including local conformance testing |
path | String | Directory of a local build (origin: development), for a path package or atmos plugin test <dir>. Inside the project it is a clean forward-slash path relative to the project directory, such as build/corporate-oidc, with symlinks resolved; outside the project it is absolute. It never contains ... Empty for every other origin |
profiles | Array of strings | Active profile names exactly as typed, without duplicates, in activation order; empty when no profile is active |
Profile names are chosen by the developer, so a rule that depends on a name can be avoided by renaming the
profile. Rules that gate trust should rely on origin and path; use profiles for conventions specific to
your organization.
The declared source is not a cryptographically authenticated publisher identity. In particular,
an offline layout can claim a repository without contacting it. Package metadata alone does not
prove who published it. Pair registry and package restrictions with
trusted publisher requirements to verify
release signatures.
Local development and offline layouts
The registry example denies both development and layout origins. Local development and
conformance testing have an empty registry; an explicit --from-layout import can claim any
registry. A matching approved host therefore does not permit an offline layout.
If the governing policy permits development packages while continuing to reject layouts, make that exception explicit:
input.origin == 'layout' || (input.origin != 'development' && !(input.registry in params.allowed_registries))
The package-name restriction still applies in the package phase. A package that sets path has no
signature, so plugins.policy.require_signature governs released packages only and the policy rules
over origin and path are the gate for local builds. Offline layout imports need their own explicit
exception in the governing policy and must still pass signature and trusted publisher checks.
To permit local builds only below approved project directories, compare the plugin directory with a list of roots:
approved-dev-roots:
on: plugin
action: deny
reason: Local plugin builds must live under an approved directory
with:
dev_roots:
- build/
rule: "input.origin == 'development' && !params.dev_roots.exists(r, input.path.startsWith(r))"
A plugin outside the project directory has an absolute path, so a relative root such as build/
never matches it.
The plugin development tutorial walks through this
rule, a rule that permits development plugins only for plugin test, a rule that requires a profile naming convention,
and an exception for a localhost registry, each run against a real project.
Enforcement
Plugin policies load from the same protected policy.d/ or ATMOS_POLICY_DIR as other policy kinds.
A denial or evaluation error blocks the operation. Another rule cannot override the denial, and a
passing CEL or Starlark rule cannot weaken plugins.policy signature or trusted publisher checks.
For organizational enforcement, keep the policy directory and Atmos runtime under trusted control.
Use an absolute ATMOS_POLICY_DIR in CI and protect the binary, environment, and governing policy
revision and project lockfile independently of candidate changes. A user who controls that runtime
can bypass its checks.
See enforcement configuration.