Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
db4a3e4
chore: consume OCI interoperability contract
ORESoftware Aug 3, 2026
df53465
feat: add deterministic OCI publication planning
ORESoftware Aug 3, 2026
830333c
feat: expose OCI publication planner
ORESoftware Aug 3, 2026
2860e57
feat: define typed OCI command surface
ORESoftware Aug 3, 2026
5d93d91
feat: expose typed OCI command arguments
ORESoftware Aug 3, 2026
a4a588e
feat: add zed oci plan command group
ORESoftware Aug 3, 2026
5ed8185
feat: dispatch credential-free OCI plans
ORESoftware Aug 3, 2026
f39df20
docs: declare OCI plan command and env flags
ORESoftware Aug 3, 2026
4b916c4
ci: validate and materialize OCI planner branch
ORESoftware Aug 3, 2026
c37cc4f
docs: document immutable OCI publication planning
ORESoftware Aug 3, 2026
5b23ccd
fix: keep OCI planning outside credential and recovery paths
ORESoftware Aug 3, 2026
028818e
ci: self-materialize OCI planner lock and formatting
ORESoftware Aug 3, 2026
d92a0c8
ci: cancel superseded OCI planner bootstrap runs
ORESoftware Aug 3, 2026
04905c6
chore: materialize OCI planner lock and formatting
github-actions[bot] Aug 3, 2026
e909b79
chore: pin finalized OCI interface contract
ORESoftware Aug 3, 2026
a5d002b
chore: remove OCI planner bootstrap workflow
ORESoftware Aug 3, 2026
8bc7d97
chore: materialize OCI planner lock and formatting
github-actions[bot] Aug 3, 2026
68b3395
feat: materialize verified local OCI layouts
ORESoftware Aug 3, 2026
90bfa35
feat: add explicit OCI layout output
ORESoftware Aug 3, 2026
fd19794
feat: export OCI layout materializer
ORESoftware Aug 3, 2026
8e02926
feat: dispatch local OCI layout materialization before auth
ORESoftware Aug 3, 2026
60f33e9
docs: define local OCI layout boundary
ORESoftware Aug 3, 2026
3b620c8
Merge OCI planner lock update into layout stack
ORESoftware Aug 3, 2026
41b161a
ci: format OCI layout candidate once
ORESoftware Aug 3, 2026
34a3fc2
ci: make one-shot OCI formatter observable
ORESoftware Aug 3, 2026
f4c14e9
ci: cancel stale one-shot formatter attempts
ORESoftware Aug 3, 2026
dc19dc0
ci: use broader pool for one-shot OCI formatter
ORESoftware Aug 3, 2026
9d1d0a8
style: format OCI layout implementation
github-actions[bot] Aug 3, 2026
c620759
chore: remove one-shot OCI formatter workflow
ORESoftware Aug 3, 2026
75e1e06
feat: add explicit ORAS OCI push command
ORESoftware Aug 4, 2026
d925bda
feat: verify and push OCI layouts through ORAS
ORESoftware Aug 4, 2026
5726944
feat: export ORAS OCI push module
ORESoftware Aug 4, 2026
2208c12
feat: dispatch authenticated ORAS OCI pushes
ORESoftware Aug 4, 2026
12c949d
feat: declare OCI ORAS push flags
ORESoftware Aug 4, 2026
90a00d3
test: clarify explicit OCI push auth parsing
ORESoftware Aug 4, 2026
38fe787
ci: validate and format ORAS push slice once
ORESoftware Aug 4, 2026
33b72ac
ci: expose one-shot ORAS validation to draft PR
ORESoftware Aug 4, 2026
570d8f0
ci: include UTF-8 and cross-platform ORAS hardening
ORESoftware Aug 4, 2026
41145ca
ci: make ORAS bootstrap edits structurally robust
ORESoftware Aug 4, 2026
7955df1
ci: satisfy Clippy in ORAS validation correction
ORESoftware Aug 4, 2026
0e71b68
style: validate ORAS push implementation
github-actions[bot] Aug 4, 2026
d25cd5f
docs: define authenticated ORAS push boundary
ORESoftware Aug 4, 2026
f76c6b6
fix: scope OCI push JSON environment separately
ORESoftware Aug 4, 2026
2eed02a
fix: align scoped OCI push JSON environment contract
ORESoftware Aug 4, 2026
6ad8600
ci: apply OCI push hardening integration fixes once
ORESoftware Aug 4, 2026
3afc238
fix: satisfy OCI push flags and README contracts
github-actions[bot] Aug 4, 2026
ec16b3e
fix: defer OCI push option combinations to runtime
ORESoftware Aug 4, 2026
315758f
ci: validate OCI push combinations at runtime once
ORESoftware Aug 4, 2026
edc95ee
ci: trigger registered OCI runtime validation
ORESoftware Aug 4, 2026
25a3a8a
ci: expose OCI runtime validation to draft PR
ORESoftware Aug 4, 2026
9edf8c6
test: avoid debugging temporary OCI credential state
ORESoftware Aug 4, 2026
d07a3f1
fix: validate OCI push option combinations at runtime
github-actions[bot] Aug 4, 2026
d2b1714
docs: clarify runtime OCI option validation
ORESoftware Aug 4, 2026
ef65679
ci: validate ORAS missing-tag classification once
ORESoftware Aug 4, 2026
7a96e43
fix: recognize ORAS registry missing tags precisely
github-actions[bot] Aug 4, 2026
1241f1c
docs: define precise missing-tag handling
ORESoftware Aug 4, 2026
1cd1c79
ci: semantically rebase OCI interop onto current main
ORESoftware Aug 4, 2026
e3e2896
ci: add semantic OCI main-sync helper
ORESoftware Aug 4, 2026
fb15de5
ci: trigger semantic OCI consolidation on current main
ORESoftware Aug 4, 2026
9c7efa4
feat: consolidate OCI interoperability on current main
github-actions[bot] Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions .cli-flags.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,7 +201,7 @@ env = "ZED_PKG_UPDATE_CHECK"
aliases = ["check"]
type = "bool"
default = "false"
help = "Check for an update without installing it."
help = "Check for an update without installing."

[flags.skip_checksum]
env = "ZED_PKG_UPDATE_SKIP_CHECKSUM"
Expand DownExpand Up@@ -301,6 +301,89 @@ help = "Emit the release plan as JSON."
[commands.release.commands.preflight]
help = "Run fixed credential-free native package preflight adapters."

[commands.oci]
help = "Plan, materialize, and distribute immutable Zed packages through OCI registries."

[commands.oci.commands.plan]
help = "Build the exact credential-free OCI publication plan."

[commands.oci.commands.plan.flags.oci_json]
env = "ZED_PKG_OCI_JSON"
aliases = ["json"]
type = "bool"
default = "false"
help = "Emit the OCI publication plan as JSON."

[commands.oci.commands.push]
help = "Verify a local OCI image layout and copy it to a registry through ORAS."

[commands.oci.commands.push.flags.oci_oras]
env = "ZED_PKG_OCI_ORAS"
aliases = ["oras"]
type = "string"
default = "oras"
help = "ORAS executable path."

[commands.oci.commands.push.flags.oci_username]
env = "ZED_PKG_OCI_USERNAME"
aliases = ["username"]
type = "string"
help = "OCI registry username; pair with --password-stdin."

[commands.oci.commands.push.flags.oci_password_stdin]
env = "ZED_PKG_OCI_PASSWORD_STDIN"
aliases = ["password-stdin"]
type = "bool"
default = "false"
help = "Read one OCI registry password or token from stdin."

[commands.oci.commands.push.flags.oci_registry_config]
env = "ZED_PKG_OCI_REGISTRY_CONFIG"
aliases = ["registry-config"]
type = "string"
help = "Explicit Docker/ORAS registry config path."

[commands.oci.commands.push.flags.oci_anonymous]
env = "ZED_PKG_OCI_ANONYMOUS"
aliases = ["anonymous"]
type = "bool"
default = "false"
help = "Push without registry credentials."

[commands.oci.commands.push.flags.oci_plain_http]
env = "ZED_PKG_OCI_PLAIN_HTTP"
aliases = ["plain-http"]
type = "bool"
default = "false"
help = "Use plain HTTP for a loopback registry only."

[commands.oci.commands.push.flags.oci_insecure_tls]
env = "ZED_PKG_OCI_INSECURE_TLS"
aliases = ["insecure-tls"]
type = "bool"
default = "false"
help = "Disable destination TLS certificate verification."

[commands.oci.commands.push.flags.oci_ca_file]
env = "ZED_PKG_OCI_CA_FILE"
aliases = ["ca-file"]
type = "string"
help = "Custom destination registry CA certificate."

[commands.oci.commands.push.flags.oci_allow_tag_replacement]
env = "ZED_PKG_OCI_ALLOW_TAG_REPLACEMENT"
aliases = ["allow-tag-replacement"]
type = "bool"
default = "false"
help = "Explicitly replace a destination tag whose digest differs."

[commands.oci.commands.push.flags.oci_json]
env = "ZED_PKG_OCI_PUSH_JSON"
aliases = ["json"]
type = "bool"
default = "false"
help = "Emit the OCI push result as JSON."

[commands.publish]
help = "Publish an artifact."

Expand DownExpand Up@@ -386,4 +469,4 @@ help = "Prune unreferenced store entries."
help = "Download cache operations."

[commands.cache.commands.clean]
help = "Clean the download cache."
help = "Clean the download cache."
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@ name = "zed_cli"
path = "src/lib.rs"

[dependencies]
zed-interfaces = { git = "https://github.com/zed-pkg/zed-interfaces.git", rev = "dc0e0a0620b9462817950b552d3d334a184b1cb1" }
zed-interfaces = { git = "https://github.com/zed-pkg/zed-interfaces.git", rev = "22dc84b509f99608d1297b210fcd3e72fa43b956" }
anyhow = "1"
clap = { version = "4.5", features = ["derive", "env"] }
clap_complete = "4.5"
Expand All@@ -36,4 +36,4 @@ uuid = { version = "1", features = ["serde", "v4"] }
walkdir = "2"
fs2 = "0.4.3"
flags2env = { git = "https://github.com/ORESoftware/flags-2-env.git", rev = "2f62e40932a0fcb8b9bf1b4c84473e34fa3c51c7" }
zip = { version = "8.6.0", default-features = false, features = ["deflate"] }
zip = { version = "8.6.0", default-features = false, features = ["deflate"] }
31 changes: 30 additions & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,9 @@ git tag v0.1.0
zed r2g # consume your own artifact before shipping (add --docker for a container)
zed publish

# derive the exact immutable OCI identity without credentials or uploads
zed oci plan oci://ghcr.io/acme/my-lib:0.1.0

# consume packages from a manifest
zed add acme/http-kit@^1
zed install
Expand DownExpand Up@@ -116,6 +119,10 @@ registry hosts both on S3/Cloudflare R2.
| `zed pack` | Build the pruned, deterministic `tar.gz` artifact |
| `zed release plan [--json]` | Print the credential-free Zed, native-registry, and forge-package release set derived from `.zpkg.toml` |
| `zed release preflight` | Validate native manifests, then run fixed credential-free package preflight adapters |
| `zed oci plan <oci://registry/repository:version> [--target <name>] [--out <layout>] [--json]` | Derive exact OCI identities and optionally materialize a verified local image layout without credentials or network transport |
| `zed oci push <layout> <oci://registry/repository:version>` | Verify a local OCI layout, copy it through ORAS using one explicit authentication mode, and require the remote tag to resolve to the expected digest |
| `zed oci plan <oci://registry/repository:version> [--target <name>] [--json]` | Pack in a temporary directory and print exact OCI config, layer, manifest, and resolved digest identities without credentials, network, or uploads |
| `zed oci push <layout> <oci://registry/repository:version>` | Verify an immutable local OCI layout, copy it through ORAS with one explicit authentication mode, and require the remote tag to resolve to the verified manifest digest |
| `zed publish` | Verify clean tree + matching VCS tag at HEAD, pack, upload |
| `zed r2g` (`zed test-local`) | Roundtrip-test your artifact: install it into a mock consumer under `~/.zed-pkg/r2g` and run `publish.smoke_test`, optionally inside an OCI container (`--docker`) |
| `zed run <bin> [args]` | Run an executable a dependency exposes via `[bin]`, with `zed_modules/.bin` on `PATH` (npx-style, no global pollution) |
Expand DownExpand Up@@ -316,6 +323,8 @@ actual CLI never drift, so it is always authoritative:
| `--frozen` | `ZED_PKG_FROZEN` | off |
| `--allow-build` (install) | `ZED_PKG_ALLOW_BUILD` | off |
| `--allow-no-manifest` / `--skip-manifest` (install) | `ZED_PKG_ALLOW_NO_MANIFEST` | off; otherwise a real-terminal confirmation is required |
| `--target` (OCI plan/polyglot install) | `ZED_PKG_TARGET` | required for a polyglot OCI plan; inferred for install when possible |
| `--json` (OCI plan) | `ZED_PKG_OCI_JSON` | off |
| `--force` (build) | `ZED_PKG_FORCE` | off |
| `--older-than` (gc) | `ZED_PKG_GC_OLDER_THAN` | `90d` |
| `--dry-run` (gc) | `ZED_PKG_GC_DRY_RUN` | off |
Expand DownExpand Up@@ -356,6 +365,26 @@ always removes the local session.

## Containers & OCI

### Immutable OCI publication plans

`zed oci plan` derives the exact OCI artifact bytes and identities before any
registry is contacted:

```sh
zed oci plan oci://ghcr.io/acme/tool:1.2.3
zed oci plan oci://ghcr.io/acme/tool-rust:1.2.3 --target rust --json
```

The input is a tagged destination. Contract v1 requires that tag to equal the
package version and rejects a caller-supplied digest. Zed validates the source
manifest and frozen dependency provenance, packs in a temporary directory, and
hashes the package archive, Zed manifest, optional lockfile, config JSON, and
OCI image manifest. The output includes the resolved immutable
`oci://...@sha256:...` reference. Planning reads no registry credentials,
performs no network request, uploads nothing, and leaves no `.zed/pack` output.
A later transport command can consume the same descriptors without changing
what is signed, attested, or pushed.

Symlinks into `$HOME/.zed-pkg` do not survive a `COPY --from=build` between
image stages, so use copy mode inside builds and cache-mount the store:

Expand DownExpand Up@@ -491,4 +520,4 @@ cargo test

## License

MIT
MIT
87 changes: 87 additions & 0 deletions docs/oci-local-layout.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
# Local OCI image-layout materialization

`zed oci plan` can materialize the exact credential-free publication plan as a
standard OCI image-layout directory when `--out` is supplied:

```sh
zed oci plan \
oci://ghcr.io/acme/tool:1.2.3 \
--out dist/tool-layout

zed oci plan \
oci://ghcr.io/acme/tool-rust:1.2.3 \
--target rust \
--out dist/tool-rust-layout \
--json
```

Without `--out`, the command remains side-effect free and prints only the
immutable publication plan introduced by `zed-pkg/zed-cli#44`.

## Output

The output follows the OCI image-layout shape:

```text
dist/tool-layout/
├── oci-layout
├── index.json
└── blobs/
└── sha256/
├── <config digest>
├── <package digest>
├── <source-manifest digest>
└── <OCI manifest digest>
```

A dependency-bearing package also includes the exact `.zpkg.lock` bytes as a
typed blob. `index.json` points at the planned OCI manifest and preserves the
requested version tag through `org.opencontainers.image.ref.name`.

The resulting directory can be inspected, archived, copied into an OCI-aware
build pipeline, or used as the source for a later authenticated ORAS/registry
transport. This slice does not log in or publish anything.

## Integrity model

The publication planner remains authoritative. Materialization:

1. builds and validates the immutable plan;
2. repacks the selected repository or polyglot target in a temporary directory;
3. reconstructs the config, package, source manifest, optional lockfile, and OCI
manifest bytes;
4. recomputes each SHA-256 and byte count;
5. rejects any difference from the plan;
6. writes each blob under `blobs/sha256/<digest>`;
7. writes `oci-layout` and `index.json` in a sibling temporary directory; and
8. renames the completed directory into the requested output path.

The command refuses to replace an existing output. A failed validation therefore
cannot partially overwrite a prior layout.

## Security boundary

Like plan-only mode, layout materialization returns before normal CLI
configuration, saved-credential resolution, or transaction recovery. It uses no
registry token, authentication session, socket, ORAS process, Docker daemon, or
Podman daemon.

The only durable write is the explicit `--out` directory. Temporary pack and
layout staging directories are removed on failure. The source project does not
receive `.zed/pack` output and pending `.zpkg-staging` state is not recovered or
mutated.

## Deliberately deferred

This PR does not implement:

- registry authentication or credential helpers;
- blob existence checks, upload, mount, or manifest push;
- tag replacement or mutable latest-tag policy;
- pull and digest-verified restore;
- OCI referrers for SPDX, CycloneDX, in-toto, or signatures;
- multi-platform image indexes across several target artifacts; or
- garbage collection of externally copied layouts.

Those remain separate contracts so a local byte-identity boundary can be tested
without network access or release credentials first.
Loading