Skip to content

Latest commit

 

History

History
328 lines (245 loc) · 12 KB

File metadata and controls

328 lines (245 loc) · 12 KB

akua.toml and akua.lock

akua's package manager is modeled on Go's two-file split but uses TOML for both files (the format Cargo, Poetry, and pnpm adopted for richer dep graphs than Go's directive syntax expresses).

  • akua.toml — what you asked for. Declared deps, version constraints, workspace members, package metadata. Human-edited.
  • akua.lock — what you got. One [[package]] entry per resolved artifact with digest + cosign signature + optional transitive dep references. Machine-maintained, never hand-edited.

Both files are TOML. Both are checked into git. Both are required.


Why two files

Clear separation of concerns:

intent evidence
file akua.toml akua.lock
edited by human akua add / akua pull / akua publish / akua update
shape small, stable may be large; churns on every resolved-version change
review focus "do we want this dep?" "is this the expected digest + signature?"

A PR that modifies akua.lock but not akua.toml is automatically suspicious (someone changed what they got without changing what they asked for). CI can lint for this.

Merged-lockfile alternatives (npm's package-lock.json, Cargo's Cargo.lock with deps embedded in Cargo.toml) bundle both concerns differently; we take Go's split (intent vs evidence in separate files) and Cargo's structured-TOML lockfile (so we can express a richer dep graph than go.sum's line-per-hash format).

Naming

  • Lowercase throughout — matches go.mod, poetry.lock, pnpm-lock.yaml, pyproject.toml (every lockfile shipped after ~2014 went lowercase).
  • .toml extension on the manifest — honest about the format; editors pick up TOML highlighting automatically.
  • No extension on the lockfile — matches Cargo.lock / poetry.lock / yarn.lock / package-lock.json convention (lockfile name is descriptive; the tool knows the format).

akua.toml

Top-level structure

[package]
name    = "my-app"
version = "0.1.0"
edition = "akua.dev/v1alpha1"                 # akua schema compat marker

# (Optional) workspace members — for monorepos with many akua packages
[workspace]
members = ["./", "./apps/*"]

# Dependencies — every import in KCL / Rego must be declared here
[dependencies]
# key = { source_type = "<ref>", version = "..." }

Dependency forms

form example use when
OCI { oci = "oci://ghcr.io/.../foo", version = "1.2.3" } published signed artifact (most common)
Git { git = "https://github.com/foo/bar", tag = "v1.2.3" } non-OCI-distributed sources
Path { path = "../shared" } workspace-local, dev-only
Replace { oci = "...", replace = { path = "../fork" } } local-fork override for debugging

Example

[package]
name    = "payments-api"
version = "3.2.0"
edition = "akua.dev/v1alpha1"

[dependencies]
# KCL sources
k8s       = { oci = "oci://ghcr.io/kcl-lang/k8s",                  version = "1.31.2" }
cnpg      = { oci = "oci://ghcr.io/cloudnative-pg/charts/cluster", version = "0.20.0" }
webapp    = { oci = "oci://ghcr.io/acme/charts/webapp",            version = "2.1.0" }

# Rego policies (compile-resolved as data. imports — see policy-format.md)
tier-prod = { oci = "oci://policies.akua.dev/tier/production",   version = "1.2.0" }
kyv-sec   = { oci = "oci://policies.akua.dev/kyverno/security",  version = "2.0.0" }

# Local fork for debugging
our-glue  = { oci = "oci://pkg.acme.internal/glue", version = "0.3.0",
              replace = { path = "../glue-fork" } }

Version resolution

  • Exact pin preferred. version = "1.2.3" means that version, nothing else. No implicit semver-range resolution.
  • Semver range allowed (version = "^1.2.0") for dependencies where minor updates are trusted. akua's resolver picks the highest matching version satisfying all constraints across the graph.
  • Conflicts error out. If two dependencies pin different versions of a shared transitive, the resolver fails with a clear message. Use replace to force a single version.

Fields

field required notes
[package].name yes a valid KCL package identifier
[package].version yes semver
[package].edition yes akua.dev/v1alpha1 for v0 compatibility
[package].strictSigning no default true; set false to permit unsigned deps (discouraged)
[workspace].members no glob patterns; enables monorepo
[dependencies] yes (can be empty) see dependency forms above

akua.lock

Cargo.lock-flavored TOML: one [[package]] entry per resolved artifact, alphabetically ordered, with optional dependencies for the transitive graph.

Format

# akua.lock — machine-maintained. Never hand-edit.
# Regenerated by `akua add`, `akua pull`, `akua publish`, `akua update`.

version = 1   # lockfile format version; bumped on incompatible changes

[[package]]
name    = "cnpg"
version = "0.20.0"
source  = "oci://ghcr.io/cloudnative-pg/charts/cluster"
digest  = "sha256:3c5d9e7f1a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
signature = "cosign:sigstore:cloudnative-pg"

[[package]]
name    = "webapp"
version = "2.1.0"
source  = "oci://ghcr.io/acme/charts/webapp"
digest  = "sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
signature = "cosign:key:acme"
dependencies = [
  "[email protected]",
]

[[package]]
name    = "common"
version = "2.20.0"
source  = "oci://ghcr.io/bitnamicharts/common"
digest  = "sha256:f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0"
signature = "cosign:sigstore:bitnamicharts"

Fields per [[package]]

field required notes
name yes matches the [dependencies] key in akua.toml
version yes exact resolved semver (not a range)
source yes full source ref: oci://…, git+https://…, or path+file://…
digest yes content-addressable hash: sha256: for OCI; sha256 over tarball for git
signature conditional cosign signature. Keyless: cosign:sigstore:<issuer>. Keyed: cosign:key:<identity>. Required unless [package].strictSigning = false in akua.toml
dependencies no ["name@version", …] — transitive edges for graph walks
attestation no SLSA attestation digest; present when the dep's author publishes one alongside
replaced no { path = "…" } when a local replace is active
yanked no true for retracted versions

Rules

  • Alphabetical order by name. Stable diffs even across unrelated PRs.
  • One [[package]] per resolved (name, version). Two major versions of the same dep means two entries.
  • No mutable metadata. No timestamps, no resolver versions, no author info. Everything is deterministic.
  • No comments in generated content. The tool-written [[package]] entries are clean; put explanations in akua.toml.
  • Trailing newline. POSIX file discipline.

What akua.lock does NOT contain

  • Source code (not a vendor directory — see akua vendor for that)
  • Version ranges (those live in akua.toml)
  • User-facing comments

Resolution workflow

akua add <kind> <ref> --version=<v>

  1. Reads current akua.toml
  2. Adds the new entry to [dependencies]
  3. Fetches the artifact; computes digest
  4. Verifies cosign signature
  5. Updates akua.lock — inserts/updates the [[package]] entry; adds transitive deps alphabetically
  6. If the new dep transitively pulls others, repeats for each

Result: both akua.toml and akua.lock updated in one atomic operation.

akua verify (CI gate)

  1. Reads akua.toml and akua.lock
  2. Resolves every dep from akua.toml
  3. Compares expected (manifest) vs locked (lockfile) digest + signature
  4. Exits 0 if everything matches; non-zero otherwise

Run in CI on every PR to catch lockfile tampering.

akua update [dep]

Updates to the highest allowed version per akua.toml constraints; rewrites the relevant [[package]] entries in akua.lock. Leaves other deps untouched unless their constraints also match a new version.

akua vendor (optional)

Writes full dependency tree to ./vendor/ for air-gapped builds. Content matches akua.lock digests. Not needed for online builds.


Workspaces

For monorepos with multiple akua packages:

platform/
├── akua.toml                    # workspace root
├── akua.lock
├── apps/
│   ├── api/
│   │   └── package.k            # member package
│   ├── worker/
│   │   └── package.k
│   └── dashboard/
│       └── package.k
└── policies/
    └── org-baseline/
        └── policy.rego          # member policy module

Workspace root akua.toml:

[workspace]
members = ["./apps/*", "./policies/*"]

[dependencies]
# Shared deps used by all members
k8s = { oci = "oci://ghcr.io/kcl-lang/k8s", version = "1.31.2" }

Members inherit workspace dependencies; they may override in a member-local akua.toml (minimal). Cross-member imports work as path-type deps.


Compatibility with kpm

akua's akua.toml is not the same as KCL's kcl.mod. We don't try to be. akua packages can contain a kcl.mod in their source tree for pure-KCL consumers who want to use upstream kcl run against the package directly; akua's resolver honors either file when it's unambiguous.

Pulling kpm-published packages. OCI deps published by kpm push (e.g. everything under ghcr.io/kcl-lang/*) work transparently through the same [dependencies] shape Helm charts use. The fetcher detects the artifact family from the manifest's media type + org.kcllang.package.* annotations, unpacks the plain tar, and registers each as a typed ExternalPkg entry inside the wasmtime render sandbox — so a Package.k can write import k8s.api.apps.v1 and have it resolve against the upstream schema bundle. See examples/10-kcl-ecosystem/ for a worked example.

See the broader architecture note for the "akua is the outer package manager; kpm is the inner KCL-layer tool" framing.


Example: a real workspace

./
├── akua.toml
├── akua.lock
├── apps/
│   ├── api/
│   │   └── package.k
│   └── worker/
│       └── package.k
├── policies/
│   └── production.rego
└── environments/
    ├── dev/inputs.yaml
    ├── staging/inputs.yaml
    └── production/inputs.yaml

akua.toml:

[package]
name    = "acme-platform"
version = "0.1.0"
edition = "akua.dev/v1alpha1"

[workspace]
members = ["./apps/*"]

[dependencies]
k8s       = { oci = "oci://ghcr.io/kcl-lang/k8s",                  version = "1.31.2" }
cnpg      = { oci = "oci://ghcr.io/cloudnative-pg/charts/cluster", version = "0.20.0" }
webapp    = { oci = "oci://ghcr.io/acme/charts/webapp",            version = "2.1.0" }
tier-prod = { oci = "oci://policies.akua.dev/tier/production",   version = "1.2.0" }
kyv-sec   = { oci = "oci://policies.akua.dev/kyverno/security",  version = "2.0.0" }

akua.lock (after resolution):

version = 1

[[package]]
name    = "cnpg"
version = "0.20.0"
source  = "oci://ghcr.io/cloudnative-pg/charts/cluster"
digest  = "sha256:d4e5f6…"
signature = "cosign:sigstore:cloudnative-pg"

[[package]]
name    = "k8s"
version = "1.31.2"
source  = "oci://ghcr.io/kcl-lang/k8s"
digest  = "sha256:a1b2c3…"
signature = "cosign:sigstore:kcl-lang"

[[package]]
name    = "kyv-sec"
version = "2.0.0"
source  = "oci://policies.akua.dev/kyverno/security"
digest  = "sha256:j0k1l2…"
signature = "cosign:sigstore:akua-release"

[[package]]
name    = "tier-prod"
version = "1.2.0"
source  = "oci://policies.akua.dev/tier/production"
digest  = "sha256:g7h8i9…"
signature = "cosign:sigstore:akua-release"

[[package]]
name    = "webapp"
version = "2.1.0"
source  = "oci://ghcr.io/acme/charts/webapp"
digest  = "sha256:m3n4o5…"
signature = "cosign:key:acme"

CI runs akua verify on every PR; any digest mismatch or missing signature fails the build.