akua / examples / 07-package-reuse
Example 07 — package reuse (cross-package composition)
One akua Package composing another. The reuser pins the base Package by OCI digest in `akua.toml`, imports its `Input` schema into its own schema (as a nested field), and renders the base's resources
One akua Package composing another. The reuser pins the base Package by OCI digest in akua.toml, imports its Input schema into its own schema (as a nested field), and renders the base's resources inline alongside its own additions.
This is what cross-package composition looks like when the full spec lands — the shape this axis is settling into. Masterplan §18 and design-notes.md §6 still list this as an open question for the final API surface. Use this example as the north-star shape; expect minor signature tweaks as the spec locks in.
The pattern
┌──────────────────────────────────────────────────────────────┐
│ platform-base │ (separate repo, OCI-published)
│ - Publishes: oci://pkg.acme.corp/platform-base:1.0 │
│ - Input schema: hostname, tls, monitoring_enabled │
│ - Produces: Deployment, Service, Ingress, ServiceMonitor│
└────────────────────┬─────────────────────────────────────────┘
│ akua.toml dependency
▼
┌──────────────────────────────────────────────────────────────┐
│ 07-package-reuse (this example) │
│ - Adds dashboard ConfigMap │
│ - Adds feature-flag env overlay │
│ - Adds one more Ingress host │
│ - Re-exports the base's Input schema as a nested field │
└──────────────────────────────────────────────────────────────┘
The reuser doesn't fork the base. Every publish of the base lands as an OCI digest; the reuser bumps its akua.toml pin and re-renders.
Layout
07-package-reuse/
├── akua.toml declares platform-base as an OCI dep
├── akua.lock digest + cosign signature of the pinned base
├── package.k the consumer — composes base + adds specifics
├── inputs.yaml sample inputs for rendering
└── README.md
The mechanism
A Package published to OCI is itself an engine source — the mental model parallel to Helm charts, kro RGDs, Kustomize bases. Any Package can be imported and composed:
import akua.pkg # the package-as-source engine callable
import packages.platform_base as base # the pinned base Package
schema Input:
"""Public input. Composes the base's schema under .base."""
# Nest the base's Input schema. Consumers fill both.
base: base.Input
# Local additions on top.
dashboard_enabled: bool = True
@ui(order=99, group="Feature flags")
experiment_X: bool = False
input: Input = ctx.input()
# Render the base Package with the nested input slice. Returns resources[].
_base = pkg.render(base.Package, input.base)
# Add local resources.
_dashboard = {
apiVersion: "v1"
kind: "ConfigMap"
metadata.name: "${input.base.appName}-dashboard"
data.enabled: input.dashboard_enabled
}
# Aggregate.
resources = [*_base, _dashboard]
Three things fall out of this shape:
- Type safety. The base's
Inputis a nested schema; misspelling a field fails at compile time with a line + column pointer. No "I forgot the base needshostname" at render time. - Pinned by digest.
akua.toml+akua.lockpin the base to a specific OCI digest. Base publishes v1.1 → you don't pick it up until youakua addexplicitly. No silent drift. - Signed provenance.
akua verifyon the consumer walks the attestation chain: the consumer's SLSA predicate includes the base's digest, which carries its own SLSA predicate, which carries the base's sources. Auditable back to the original chart authors.
Running it
akua add # resolves deps → writes akua.lock
akua render --inputs inputs.yaml # composes base + local additions
akua inspect oci://pkg.acme.corp/platform-base:1.0 # peek at what we're pinning
When to reuse vs fork
Reuse when the base captures a genuine shared convention: your org's production-ready webapp shape, a standard observability stack, a licensed vendor package you subscribe to.
Fork when you need base-level invariants that don't exist yet. Forking means copying the base's Package.k into your workspace and editing it. You lose the upgrade path; you own the full surface.
Don't reuse to avoid learning KCL. If the base's author didn't anticipate your override, reuse leads to a pile of postRenderer hacks that are harder to maintain than a fork.
Open-question addendum
This axis is specced to the shape shown above but the exact signature of pkg.render(Package, inputs) vs alternatives (base.render(input.base), auto-unwrapping imports, etc.) may iterate before the spec locks in. See masterplan §18 open question 6 and design-notes.md §6. Consumers of this example: don't hard-code the exact callable name in skills or training material yet.
See also
- package-format.md §2 Imports — where package-vs-chart imports are described
- lockfile-format.md — how OCI-pinned Packages flow through
akua.toml - 06-multi-engine/ — Helm / Kustomize / kro — the other engine sources;
pkgis the fourth - 02-webapp-postgres/ — the shape of the base Package before it was reused here
package.k
# Example 07 — package reuse
#
# Composes a base Package (pinned by OCI digest in akua.toml) and adds local
# resources on top. The base's Input schema nests into this Package's Input,
# preserving end-to-end type safety.
#
# Render:
# akua add
# akua render --inputs inputs.yaml
import akua.ctx
import akua.pkg # package-as-source engine callable
import akua.ui as ui
import packages.platform_base as base # pinned via akua.toml / akua.lock
schema Input:
"""Public input. Composes the base's schema under .base."""
# Nest the base's Input. Consumers must fill both the base's required
# fields and our local additions below.
base: base.Input
@ui(order=90, group="Feature flags")
dashboard_enabled: bool = True
"""Enable the local experimental dashboard overlay."""
@ui(order=91, group="Feature flags")
experiment_X: bool = False
"""Roll out experiment X to this tenant."""
@ui(order=92, group="Routing")
extra_hostnames: [str] = []
"""Additional hostnames beyond the base's primary hostname."""
check:
len(extra_hostnames) < 10, "at most 10 extra hostnames"
input: Input = ctx.input()
# --- Base composition ---
# `pkg.render(Package, inputs)` is the package-as-engine callable, parallel
# to `helm.template(chart, values)`. Returns the base's resources[] list.
_base = pkg.render(base.Package, input.base)
# --- Local additions ---
# Experimental dashboard ConfigMap, gated by the local feature flag.
_dashboard = {
apiVersion: "v1"
kind: "ConfigMap"
metadata = {
name: "${input.base.appName}-dashboard"
namespace: input.base.appName
labels.feature: "dashboard"
}
data = {
enabled: str(input.dashboard_enabled)
experiment_X: str(input.experiment_X)
}
} if input.dashboard_enabled else Undefined
# Extra Ingresses for every additional hostname, all pointing at the same
# Service the base created.
_extra_ingresses = [
{
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata = {
name: "${input.base.appName}-extra-${i}"
namespace: input.base.appName
}
spec = {
rules = [{
host = host
http.paths = [{
path: "/"
pathType: "Prefix"
backend.service = {
name: input.base.appName
port.number: 80
}
}]
}]
}
}
for i, host in input.extra_hostnames
]
# --- Aggregate ---
# The base's resources flow through unchanged. Local additions compose on top.
# The order in `resources` is irrelevant — reconcilers derive a graph from
# owner references, not list position.
resources = [*_base, _dashboard, *_extra_ingresses]
# Metadata for `akua inspect` and audit surfaces.
metadata = {
name: "checkout-with-dashboard"
version: "0.1.0"
description: "platform-base + experimental dashboard + multi-host routing"
publisher: "github.com/acme/checkout"
# The walk that `akua verify` follows: this Package's attestation
# references the base's attestation, which references its source charts.
extends: ["oci://pkg.acme.corp/platform-base@sha256:c7e4b8a1..."]
}
Source: examples/07-package-reuse/