From cbc2bd31960faceaef8c37beb2d18460a0a69415 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Tue, 2 Jun 2026 18:59:47 -0700 Subject: [PATCH 1/9] =?UTF-8?q?docs:=20add=20MkDocs=20documentation=20site?= =?UTF-8?q?=20with=20Di=C3=A1taxis=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a Material for MkDocs documentation site under docs/ with a 17-page Diátaxis information architecture: audience-grouped Developer and Operator guides over generic Reference, Recipes, Troubleshooting, and Concepts sections. Migrates docs/host-access.md into developer/connecting-tools.md. Tooling: mkdocs.yml (Material theme, collapsing left-sidebar nav), Moon tasks root:docs and root:docs-serve (mkdocs run through uv), and a GitHub Pages CI workflow (build --strict on PRs, deploy to Pages on master). Drafted grounded in the code and verified end to end: mkdocs build --strict is clean, CLI flags/defaults match the binary, all recipe manifests byte-match examples/ and pass CRD-schema + dry-run validation, and the documented commands were run against a live yacd devnet. The live run corrected two host-workflow bugs the static drafts missed: yacd topup must be wrapped in yacd run to reach the cluster-internal faucet, and yacd list needs -A to show a network in its own namespace. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/docs.yml | 47 +++ .gitignore | 5 +- docs/concepts/architecture.md | 207 +++++++++++++ docs/concepts/security.md | 167 +++++++++++ docs/developer/connecting-tools.md | 154 ++++++++++ docs/developer/funding.md | 124 ++++++++ docs/developer/getting-started.md | 164 +++++++++++ docs/developer/networks.md | 146 ++++++++++ docs/host-access.md | 135 --------- docs/index.md | 54 ++++ docs/operator/db-sync.md | 162 +++++++++++ docs/operator/installation.md | 112 ++++++++ docs/operator/testing.md | 111 +++++++ docs/recipes.md | 251 ++++++++++++++++ docs/reference/cardanodbsync.md | 329 +++++++++++++++++++++ docs/reference/cardanonetwork.md | 340 ++++++++++++++++++++++ docs/reference/cli.md | 447 +++++++++++++++++++++++++++++ docs/reference/configuration.md | 179 ++++++++++++ docs/reference/environment.md | 118 ++++++++ docs/troubleshooting.md | 263 +++++++++++++++++ mkdocs.yml | 58 ++++ moon.yml | 14 + 22 files changed, 3451 insertions(+), 136 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/concepts/architecture.md create mode 100644 docs/concepts/security.md create mode 100644 docs/developer/connecting-tools.md create mode 100644 docs/developer/funding.md create mode 100644 docs/developer/getting-started.md create mode 100644 docs/developer/networks.md delete mode 100644 docs/host-access.md create mode 100644 docs/index.md create mode 100644 docs/operator/db-sync.md create mode 100644 docs/operator/installation.md create mode 100644 docs/operator/testing.md create mode 100644 docs/recipes.md create mode 100644 docs/reference/cardanodbsync.md create mode 100644 docs/reference/cardanonetwork.md create mode 100644 docs/reference/cli.md create mode 100644 docs/reference/configuration.md create mode 100644 docs/reference/environment.md create mode 100644 docs/troubleshooting.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..8ea0878f --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,47 @@ +name: docs + +on: + pull_request: + paths: + - docs/** + - mkdocs.yml + push: + branches: + - master + paths: + - docs/** + - mkdocs.yml + +permissions: + contents: read + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 + - name: Build docs + run: uv run --with mkdocs-material==9.7.6 mkdocs build --strict + - uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/configure-pages@v5 + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 49b36de7..631ea3d7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,7 @@ coverage.html *.swp # Claude -.claude/scheduled_tasks.lock \ No newline at end of file +.claude/scheduled_tasks.lock + +# Docs site build output +site/ \ No newline at end of file diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md new file mode 100644 index 00000000..58ff9d04 --- /dev/null +++ b/docs/concepts/architecture.md @@ -0,0 +1,207 @@ +# Architecture + +YACD is a Kubernetes-native development environment manager for Cardano. It is +built for people developing applications on Cardano, not for validators, stake +pool operators, or production network operators. This page explains how the +system is put together and why it is shaped the way it is. For step-by-step +instructions see the [developer](../developer/getting-started.md) and +[operator](../operator/installation.md) guides; for exhaustive field and flag +facts see the [reference](../reference/cardanonetwork.md) section. + +## Two artifacts: operator and CLI + +YACD ships as two cooperating pieces: a Kubernetes operator and a companion +`yacd` CLI. The split is deliberate, and it follows the grain of Kubernetes +itself. + +The operator owns **declarative cluster state**. It reconciles long-lived +desired state: node workloads, generated or fetched configuration, genesis +material, supporting services, persistent volumes, Secrets, ConfigMaps, +Services, and the status that reports whether all of that is healthy. If you can +sensibly express it as "this environment should exist, with these services +enabled," it belongs to the operator. The operator watches its custom resources +across namespaces and continuously drives the cluster toward the spec. + +The CLI owns **imperative developer workflow**. It compiles a single +developer-facing config file into the Kubernetes resources the operator +consumes, applies them, waits for readiness, prints connection details, +forwards endpoints to your host, and runs one-off actions like topping up a +wallet. These are ad hoc commands, not desired state. + +The reason for the boundary is that Kubernetes is an excellent model for desired +state and a poor model for one-off actions. "This network should run with +db-sync enabled" is a natural fit for a controller that reconciles toward it +forever. "Top this address up right now" is not: encoding a single funding +request as a resource mutation would be awkward and would leave stale objects +behind. So durable state lives in CRDs and the operator, and transient actions +live in the CLI. The CLI is a Kubernetes client; it holds no privileged state of +its own and talks to the same API server any other client would. + +## CardanoNetwork: the primary resource + +The primary custom resource is `CardanoNetwork` (API group `yacd.meigma.io`, +version `v1alpha1`). It is intentionally environment-shaped rather than +node-shaped: it describes a Cardano *network* and the chain-access services +exposed beside it, even though the first runtime reconciles a single primary +node. + +A `CardanoNetwork` owns the core Cardano substrate. Its spec carries the network +`mode` (`local` or `public`), the primary `node` workload (shared by both +modes), the mode-specific `local` or `public` block, and a `chainAPI` block for +the services exposed next to the node. The controller reconciles the node into a +workload backed by a PVC for the node database, publishes resolved network +identity and cluster-local Service endpoints into status, and tracks health +through a set of `metav1.Condition` entries (`Ready`, `NodeReady`, +`NodeSynchronized`, `OgmiosReady`, `KupoReady`, `FaucetReady`, `WalletReady`, +`ArtifactsReady`, and others). Status reports only what the controller can +observe in-cluster, so consumers do not trust stale or hand-edited values. + +Two chain-access services are enabled by default, because a raw `cardano-node` +is not enough to build against. [Ogmios](https://ogmios.dev) is the default +chain-access API: it gives YACD and developers a JSON/RPC interface for query, +submit, and evaluate without every client having to share the node's Unix +socket. [Kupo](https://cardanosolutions.github.io/kupo/) is the default chain +index. A local-only faucet is also available but is **opt-in**, because it +exposes a spending endpoint; it must be explicitly enabled. For the exact +defaults, ports, and images, see the +[CardanoNetwork reference](../reference/cardanonetwork.md). + +## Supporting-service CRDs: CardanoDBSync + +Heavier services are modeled as **separate CRDs with separate controllers**, +rather than as ever-growing fields on `CardanoNetwork`. The first such service +is `CardanoDBSync`, which runs +[cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync) and its +Postgres database. + +A supporting CRD references a same-namespace `CardanoNetwork` through its +`networkRef` and derives chain information from that network rather than +re-declaring it. This keeps each controller focused: the db-sync controller +reasons about db-sync, Postgres, and ledger state, while the network controller +reasons about the Cardano substrate. It also keeps ownership clean. Enabling +db-sync does not have to disturb the primary node, and the db-sync controller +owns its own workload, storage, database wiring, config, and status. + +The default placement is a **dedicated follower node**. Rather than forcing the +primary node to share its Unix socket, the db-sync controller runs its own +follower `cardano-node` colocated with db-sync in one workload. The follower +joins the primary network over ordinary node-to-node TCP and exposes a local +socket that db-sync consumes inside the same Pod. This costs extra CPU, memory, +storage, and startup time, but it preserves controller isolation: the primary +node never restarts because you added an indexer. + +A `primarySidecar` placement mode is the explicit exception. When the cost of a +duplicate node outweighs the benefit of isolation, db-sync can be composed +directly into the primary node Pod and consume the primary socket. That trades +isolation for a single node copy and rolls the primary workload when the +attachment changes; it is supported for local and non-mainnet public profiles, +but not for public mainnet. For the field-level details of both modes, see the +[CardanoDBSync reference](../reference/cardanodbsync.md) and the +[db-sync operator guide](../operator/db-sync.md). + +## Why a Unix socket forces this shape + +Many Cardano tools want direct access to a node's Unix socket, and that single +fact drives much of the architecture. A Unix socket is a local-filesystem IPC +object. It is not a cluster-wide endpoint and cannot be exposed through a +Kubernetes Service. The robust pattern is to share it *within one Pod* using an +ephemeral volume: the node data directory lives on a PVC, while the socket +directory is ephemeral and mounted by sidecars in the same Pod. + +This is why Ogmios runs as a sidecar that mounts the node socket and re-exposes +chain access as a network API, and why socket-hungry services like db-sync +default to a colocated follower node instead of reaching across Pods. YACD +deliberately avoids RWX PVCs and hostPath socket sharing, which are fragile, +scheduler-sensitive, and a poor fit for shared clusters. The same constraint +shows up at the CLI boundary: tools that speak a network protocol can be +port-forwarded to your host, but a tool that needs the node socket directly +(notably `cardano-cli`) must run *inside* the node Pod. + +## Local vs public networks + +The two network modes differ mostly in where chain material comes from. + +A **local** network is generated and owned by YACD. The controller produces +fresh genesis and node configuration in-cluster from the spec: network magic, +ledger era, slot and epoch timing, generated stake-pool topology, and a curated +genesis profile (for example a zero-fee preset for fast tests). The generated +artifacts are staged and served over HTTP from the node's PVC by a small +cardano-tools serve endpoint, alongside a `manifest.json`, so the node and any +follower nodes consume the same configuration from one source of truth. The +`ArtifactsReady` condition reports when that bundle is staged and being served. + +A **public** network joins a known profile: `preprod`, `preview`, or `mainnet`. +Instead of generating genesis, the controller fetches the published +configuration for the profile. For most profiles the node can sync from genesis, +but mainnet is far too large to sync that way in a development setting. For +mainnet, the spec requires a [Mithril](https://mithril.network) bootstrap: an +init container uses a Mithril client to download and verify a Cardano database +snapshot, seeding the node's PVC so the node starts from a recent point instead +of from genesis. The same HTTP artifact-serving pattern applies, so resolved +configuration is reachable in-cluster. + +!!! warning "Mainnet is gated" + A public mainnet `CardanoNetwork` requires a Mithril bootstrap, large + persistent storage, and an explicit opt-in (`--allow-mainnet` on the CLI). + It is not the default development path. See the + [CardanoNetwork reference](../reference/cardanonetwork.md) for the + bootstrap and storage fields. + +## Host access: bridging the cluster to your host + +The services a `CardanoNetwork` exposes are **cluster-internal** Service URLs. +That is correct for in-cluster consumers and for supporting controllers, but a +developer's tests and tools usually run on the host. The CLI's host-access verbs +bridge that gap. + +`run`, `connect`, and `exec` are the three ways across the boundary. `run` and +`connect` both establish supervised port-forwards from the cluster's chain-API +Services to loopback on your host, but they surface those loopback URLs +differently. `run` wraps a single command (the primary CI path), exposing the +loopback URLs through a small `YACD_*` environment contract so tests read +ordinary environment variables instead of parsing a YACD file. `connect` holds +the forwards open in one terminal while you work in another, writing the +loopback URLs to an `endpoints.json` file instead of wiring an environment. + +`exec` is different, and it exists because of the socket constraint above. Tools +that speak a network protocol can be forwarded, but `cardano-cli` reaches the +node over its local Unix socket, which cannot be forwarded as a TCP port. So +`exec` runs the command *inside* the primary node Pod, where +`CARDANO_NODE_SOCKET_PATH` and the `YACD_*` variables are already set. The rule +is simple: forward network APIs to the host with `run`/`connect`; run +socket-bound tools in-pod with `exec`. For the verb behaviors, the `YACD_*` +table, and the `endpoints.json` schema, see the +[CLI reference](../reference/cli.md) and the +[connecting tools guide](../developer/connecting-tools.md). + +!!! warning "The faucet is local-only and host-gated" + The faucet funds addresses on local development networks only. The CLI + forwards it to loopback and exempts the loopback URL from its trust gate; + targeting a custom non-loopback faucet URL from the host requires explicit + trust flags so the bearer token is never sent to an unexpected host. See the + [funding guide](../developer/funding.md) and the + [security model](security.md). + +## Developer and operator workflows + +The same system serves two overlapping audiences. + +A **developer** typically authors one developer-facing config file, checks it +into a repository, and drives it with the CLI: render and apply the network, +wait for readiness, fund a wallet, and point tests at the forwarded endpoints. +They rarely hand-author CRDs; the CLI compiles their single config into the +decomposed Kubernetes resources the operator expects, giving them one pane of +glass while preserving clean resource boundaries in the cluster. + +An **operator** installs and runs the YACD operator in a cluster (locally on +[k3d](https://k3d.io) or in a shared cluster), manages the manager Deployment +and its RBAC through the Helm chart, and reasons directly about CRDs, status +conditions, placement modes, and capacity for heavier services like db-sync. + +The two workflows overlap because they act on the same resources through the +same API server. A developer's `yacd up` produces the very `CardanoNetwork` an +operator can inspect with `kubectl`; an operator's installed controller is what +makes a developer's config become a running network. The CLI is a convenience +layer over the cluster API, not a separate control plane. For the operator's +trust boundaries and the host-access trust gates, see the +[security model](security.md). diff --git a/docs/concepts/security.md b/docs/concepts/security.md new file mode 100644 index 00000000..1d3c7d8b --- /dev/null +++ b/docs/concepts/security.md @@ -0,0 +1,167 @@ +# Security model + +YACD is a development environment manager, so its threat model is shaped by two +facts: the faucet is a live spending endpoint, and the operator runs with +cluster-wide reach. This page explains the deliberate security decisions that +follow from those facts: why the faucet auth token never leaves the host, why +`yacd topup` gates custom faucet URLs, what the cluster-scoped manager RBAC +means on a shared cluster, and how the chart's optional image-verification path +hardens the supply chain. For the concrete knobs, follow the links to the +how-to and reference pages. + +## The faucet auth token is host-only + +A network with the faucet enabled publishes a Kubernetes Secret holding a Bearer +token. Any request that spends from the faucet must carry that token, so the +token is a credential worth protecting. + +YACD treats the token as **host-only**: it is read on the developer's machine, +used to authenticate faucet calls over a port-forward, and exposed to host +tooling through the `YACD_FAUCET_TOKEN` environment variable. It is **never** +injected into a process running inside the cluster. + +That asymmetry is intentional. The in-pod execution path (`yacd exec`) builds +its `YACD_*` environment without the token. From the source comment that +documents the omission: + +> It intentionally omits `YACD_FAUCET_TOKEN` — a Bearer token injected into the +> exec argv would land in apiserver audit logs and /proc, and in-pod tooling +> does not need it. + +Two leak channels drive the decision: + +- **apiserver audit logs.** Launching an in-pod process means handing an `exec` + request to the Kubernetes API server. If the token rode in the argv or + environment of that request, it would be captured verbatim in the cluster's + audit log, where it persists and is visible to anyone with audit access. +- **`/proc`.** A process's argv and environment are readable through `/proc` by + other processes in the same pod or by anything with host visibility, so a + token placed there is exposed for the lifetime of the process. + +In-pod tooling has no need for the token in the first place: it talks to the +cluster-internal Ogmios, Kupo, and node socket directly. Keeping the token on +the host removes the credential from both leak channels without losing any +capability. + +!!! warning "The faucet is local-only" + The faucet exposes a spending endpoint and is intended for local + development networks only. The host-only token rule is part of why: it keeps + a spending credential off the cluster's API surface. + +## The `yacd topup` trust gate + +`yacd topup` sends the faucet auth token to a faucet URL. By default that URL is +the one the `CardanoNetwork` published in its status, reached over a loopback +port-forward. The `--faucet-url` flag lets you override that destination, which +reintroduces the risk of sending a live credential somewhere it should not go. + +A trust gate guards the override. It only engages when you actually point +`--faucet-url` at something that differs from the published URL and is not a +loopback target. When the URL matches what the cluster published, or resolves to +`localhost` or a loopback IP, the gate stays out of your way. Outside those +cases it defends three distinct attack vectors, each with its own +acknowledgement flag. + +### Vector 1: token exfiltration to an attacker-supplied host + +`--faucet-url` accepts any HTTP endpoint. If you (or a script you ran) pointed it +at an attacker-controlled host, the request would carry the Bearer token +straight to that host. The gate's first job is to notice that the destination is +neither the cluster-published URL nor a loopback target, and to refuse rather +than leak the token to an unknown host. + +### Vector 2: accidental exposure via a non-loopback override + +Even a legitimate remote operator may not realise that a custom `--faucet-url` +leaves the cluster boundary. A custom non-loopback destination is therefore +refused by default; sending the token there requires the explicit +acknowledgement `--trust-faucet-url`. The error names the Secret and the +destination host so you can confirm you meant it: + +```text +refusing to send faucet auth Secret / token to custom faucet URL host ""; pass --trust-faucet-url to allow this destination +``` + +### Vector 3: plaintext eavesdropping + +A trusted custom URL using `http://` would transmit the Bearer token in +cleartext, where any on-path observer can read it. The gate is HTTPS-only by +default even for a trusted destination, so an `http://` custom URL needs a second +acknowledgement, `--allow-insecure-faucet-url`, on top of `--trust-faucet-url`: + +```text +refusing to send faucet auth Secret / token to insecure custom faucet URL host ""; pass --allow-insecure-faucet-url with --trust-faucet-url to allow HTTP +``` + +The two flags compound: trust authorises the destination, and the insecure flag +separately authorises plaintext to it. Neither one implies the other, so you +cannot send a token in cleartext to a remote host by accident. For the flag +defaults and the full `topup` surface, see the [CLI reference](../reference/cli.md); +for the funding workflow, see the [funding how-to](../developer/funding.md). + +## Cluster-scoped manager RBAC + +The chart binds the manager's ServiceAccount to a `ClusterRole`, not a +namespaced `Role`. The grant is broad by design: the operator manages +`CardanoNetwork` and `CardanoDBSync` objects and reconciles them into the core +Kubernetes workloads they need. + +The `ClusterRole` grants, across all namespaces: + +- `configmaps` and `persistentvolumeclaims`: create, get, list, patch, update, + watch +- `pods`: get, list +- `secrets`: create, delete, get, list, patch, watch +- `services`: create, delete, get, list, patch, update, watch +- `deployments` (`apps`): create, get, list, patch, update, watch +- `cardanonetworks` and `cardanodbsyncs` (`yacd.meigma.io`): get, list, watch, + plus get, patch, update on their `/status` subresources + +The implication for a **shared cluster** is direct: a YACD install can create, +read, and delete Secrets, Services, ConfigMaps, PVCs, and Deployments in any +namespace, because that is the scope the reconcilers operate at. The `secrets` +verbs include `create` and `delete` because the faucet auth Secret is a managed +object. Treat the operator as a cluster-wide actor when you decide where to +install it; an isolated development cluster is the intended home, not a +multi-tenant production cluster shared with untrusted workloads. + +RBAC creation is on by default but optional (`rbac.create`), and the role and +binding names are templated so an operator can substitute externally managed +RBAC. See the [installation guide](../operator/installation.md) and the +[configuration reference](../reference/configuration.md) for those values. + +## Supply-chain image verification + +The release workflow attests the manager image, the faucet image, and the Helm +chart with GitHub-native (Sigstore keyless) attestations. The chart ships an +**opt-in** Kyverno `ClusterPolicy` that verifies the manager and faucet image +attestations at Pod admission time, closing the gap between "an image is signed +somewhere" and "only verified images run in this cluster". + +The policy is **disabled by default** (`kyverno.imageVerification.enabled: +false`) because it depends on a running [Kyverno](https://kyverno.io) +installation. When you enable it, Kyverno intercepts pod admission and rejects +images that do not carry a valid keyless Sigstore attestation matching the +configured attestor. + +The attestor defaults encode trust in the `meigma/yacd` release pipeline rather +than in a long-lived key: + +- **issuer** `https://token.actions.githubusercontent.com` — the OIDC identity + must come from GitHub Actions. +- **subjectRegExp** restricts the signing workflow to `meigma/yacd`'s + `release.yml` running on a `v` tag, so a signature produced by any + other workflow or fork does not satisfy the policy. +- **Rekor** at `https://rekor.sigstore.dev` — the transparency log that records + the keyless signing event. +- **attestation** of type `https://slsa.dev/provenance/v1` with build type + `https://actions.github.io/buildtypes/workflow/v1` — the policy checks SLSA + provenance, not just a bare signature. + +Because the signing identity is keyless and tied to a specific workflow on a +release tag, there is no signing key to leak or rotate; trust flows from the +GitHub OIDC identity and the public transparency log. The issuer, subject +pattern, Rekor URL, validation failure action, and the set of image references +the policy applies to are all configurable under `kyverno.imageVerification.*`. +To enable and tune verification, see the [installation guide](../operator/installation.md); +for the full value surface, see the [configuration reference](../reference/configuration.md). diff --git a/docs/developer/connecting-tools.md b/docs/developer/connecting-tools.md new file mode 100644 index 00000000..3542c563 --- /dev/null +++ b/docs/developer/connecting-tools.md @@ -0,0 +1,154 @@ +# Connecting tools & tests + +The operator publishes a network's chain APIs as **cluster-internal** +`*.svc.cluster.local` Service URLs that a laptop or CI runner cannot reach +directly. Use `yacd run`, `yacd exec`, and `yacd connect` to bridge that gap. +Each verb sets the same `YACD_*` environment variables, so your tools read +ordinary env vars and stay YACD-agnostic across local and CI runs. + +This page covers choosing and using the verbs. For the full variable list and +defaults, see the [CLI reference](../reference/cli.md). All examples assume the +network is already `up` and `Ready`. + +## Choose a verb + +Pick the verb by how your tool reaches the node and how long you need access: + +| You want to… | Use | Why | +|---|---|---| +| Run a test suite or one-off command against Ogmios, Kupo, or the faucet | [`yacd run`](#run-a-test-runner) | Forwards the TCP Services to loopback ports, injects `YACD_*`, runs your command, then tears the forwards down. | +| Run `cardano-cli` or any tool that needs the node's Unix socket | [`yacd exec`](#run-a-socket-bound-tool) | Runs the command inside the node Pod, where the socket is local. A port-forward cannot expose a Unix socket. | +| Keep endpoints open for a dev server, REPL, or repeated IDE test runs | [`yacd connect`](#hold-forwards-open) | Holds supervised forwards open in one terminal and writes the loopback URLs to `endpoints.json` for other processes to read. | + +The deciding factor between `run` and `exec` is the transport: **`run` forwards +TCP** (Ogmios, Kupo, faucet) to loopback ports; **`exec` runs in the Pod** for +anything that speaks to the node over its local Unix socket. + +## Run a test runner + +Use `yacd run NAME -- ` for the primary test/CI path. It establishes scoped +port-forwards, injects the `YACD_*` environment, runs `` on the host, and +tears the forwards down when it exits. The command's exit code is propagated, so +a test failure survives the wrapper. + +Put `--` before any command that takes its own flags so they pass through to the +command instead of being parsed by `yacd`: + +```sh +yacd run my-net -- go test ./e2e/... +``` + +Your test code reads the loopback URLs from the environment: + +```go +ogmios := os.Getenv("YACD_OGMIOS_URL") // ws://127.0.0.1: +kupo := os.Getenv("YACD_KUPO_URL") // http://127.0.0.1: +``` + +With no command, `run` drops into your `$SHELL` (falling back to `/bin/sh`) with +the same environment set, which is handy for poking at the network by hand: + +```sh +yacd run my-net +``` + +If a forward drops while the command is running, `run` cancels the command and +reports the lost connection instead of a bare exit code. + +See the [`YACD_*` variable table](../reference/cli.md) for every variable and +when it is present. + +## Run a socket-bound tool + +Use `yacd exec NAME -- ` for `cardano-cli` and anything that reaches the +node over its local Unix socket. `cardano-cli` talks to the node over a +`--socket-path`, not over TCP, so a port-forward cannot expose it. `exec` runs +the command **inside** the primary node Pod with `CARDANO_NODE_SOCKET_PATH` and +the `YACD_*` variables set in the pod environment, so `cardano-cli` finds the +socket automatically: + +```sh +yacd exec my-net -- cardano-cli query tip --testnet-magic 42 +``` + +`exec` runs the command directly, **not** through a shell, so `$VAR` references +in arguments are not expanded. To interpolate `YACD_*` variables into arguments, +run a shell explicitly: + +```sh +yacd exec my-net -- sh -c 'cardano-cli query tip --testnet-magic "$YACD_NETWORK_MAGIC"' +``` + +When both stdin and stdout are a terminal, `exec` attaches an interactive TTY +(raw mode, with window resizes forwarded), so this opens an interactive shell +inside the node Pod. Piped or non-terminal invocations (for example in CI) +stream without a TTY: + +```sh +yacd exec my-net -- sh +``` + +!!! warning "The faucet token is host-only" + `YACD_FAUCET_TOKEN` is never set in the `exec` environment. A Bearer token + on the in-pod command line would leak into apiserver audit logs. Tools that + need the token must run on the host under `run` or `connect`. + +## Hold forwards open + +Use `yacd connect NAME` when several host processes need the endpoints at once, +or across repeated runs: a dApp dev server, a REPL, or an IDE that re-runs tests. +It holds supervised forwards open in the foreground (one terminal) and writes the +loopback URLs to a gitignored endpoint state file for other processes to read. +Run it in one terminal and your tools in another: + +```sh +yacd connect my-net +``` + +`connect` runs until you press Ctrl-C. If a forward drops (pod restart, idle +timeout) it re-resolves the primary Pod, reassigns local ports, and writes a +fresh endpoints file. A clean disconnect removes the file. + +The endpoint state file lives under `.yacd/`. When the namespace defaults to the +network name, the path is: + +```text +.yacd//endpoints.json +``` + +When `--namespace` is set, the path includes both identity parts so networks +with the same name in different namespaces do not collide: + +```text +.yacd///endpoints.json +``` + +Read the loopback URLs from that file in any host process. The file is created +`0600` under `0700` directories, its ports are only live while `connect` is +running, and it **never contains the faucet token**. For the document's field +names and shape, see the [endpoints.json schema](../reference/cli.md). + +## Fund an address from a test + +`yacd topup NAME --address ADDR --lovelace N --await` funds `ADDR` through the +faucet and polls Kupo until the funding transaction's output appears, so a test +never races chain inclusion. `--await` requires a Kupo URL: pass `--kupo-url`, +or run under `yacd run`, which sets `YACD_KUPO_URL` (the default `--kupo-url` +source): + +```sh +export ADDR=addr_test1... # the address your test funds +yacd run my-net -- sh -c \ + 'yacd topup my-net --address "$ADDR" --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL" --await' +``` + +The loopback faucet URL from a `run` or `connect` forward is exempt from the +`topup` trust gate, so no `--trust-faucet-url` flag is needed against it. See +the [CLI reference](../reference/cli.md) for all `topup` flags. + +## See also + +- [CLI reference](../reference/cli.md) — every flag, the `YACD_*` variable + table, and the `endpoints.json` schema. +- [Architecture](../concepts/architecture.md) — why the contract is shaped this + way. diff --git a/docs/developer/funding.md b/docs/developer/funding.md new file mode 100644 index 00000000..ebcfeefe --- /dev/null +++ b/docs/developer/funding.md @@ -0,0 +1,124 @@ +# Fund an account on a local network + +Get test ADA into an address on a local network in three steps: enable the +faucet on the Environment, find the address and faucet details with `yacd info`, +then send lovelace with `yacd topup`. + +!!! warning "The faucet is local-only" + The faucet exposes a spending endpoint, so it is opt-in and intended for + local development networks. The faucet auth token is host-only and is never + placed in the in-pod environment. See + [Security](../concepts/security.md) for the trust model. + +## 1. Enable the faucet + +The faucet is off by default. Turn it on in the network's `chainAPI`. Optionally +enable the pre-funded developer wallet too, so you have a funded address from day +zero (the wallet requires the faucet and Kupo, and is local-mode only): + +```yaml +spec: + network: + chainAPI: + faucet: + enabled: true + wallet: + enabled: true +``` + +Apply the manifest and wait for the network to become `Ready`. The faucet and +wallet defaults (sources, per-request lovelace bounds, wallet funding amount) +are documented in +[the CardanoNetwork reference](../reference/cardanonetwork.md). A complete +copy-paste manifest lives in the [recipes](../recipes.md). + +`topup` refuses to run until the network publishes a faucet: it requires the +`Ready` and `FaucetReady` conditions to be `True` and fresh, and it reads the +faucet endpoint and auth Secret straight from status. + +## 2. Find the address and faucet details + +Print the network's status and connection information: + +```sh +yacd info my-net +``` + +The text output includes an `Endpoints` section with the `faucet` URL, a +`Faucet` section with the auth Secret name, and — when the developer wallet is +enabled — a `Wallet` section with its `addr_test...` address and funded state: + +```text +Wallet: + Address: addr_test1... + Key Secret: my-net-wallet-keys + Funded: true +``` + +Use that wallet `Address` as your funding target, or supply any other testnet +address you control. Add `--json` for machine-readable output: + +```sh +yacd info my-net --json +``` + +## 3. Send lovelace + +The faucet is a cluster-internal Service, so run `topup` through `yacd run`, +which forwards the faucet to a local port and exposes it as `$YACD_FAUCET_URL`: + +```sh +yacd run my-net -- sh -c \ + 'yacd topup my-net --address addr_test1... --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL"' +``` + +`--address` and `--lovelace` are both required, and `--lovelace` must be greater +than zero and within the faucet's configured min/max bounds. `topup` reads the +auth token from the published Secret automatically, and the loopback URL that +`run` exposes is exempt from the [trust gate](../concepts/security.md). On +success it prints the transaction ID, source, lovelace, and destination. Add +`--json` for a machine-readable result. + +!!! note "Running `topup` without `run`" + Without `--faucet-url`, `topup` targets the faucet URL the cluster published + (`http://-faucet..svc.cluster.local:`). That name + resolves only from inside the cluster, so a bare `yacd topup` works for + in-cluster callers (such as a CI job running in a Pod) but not from your + host. From your host, bridge it with `yacd run` as shown above. + +The full `topup` flag set — including `--source`, `--faucet-url`, the +`--trust-faucet-url` / `--allow-insecure-faucet-url` trust gates, and the +`--await` options — is documented in +[the CLI reference](../reference/cli.md). + +## Confirm on-chain + +By default `topup` returns as soon as the faucet accepts the request. To block +until the funding transaction is actually confirmed on-chain, add `--await`, +which polls [Kupo](https://cardanosolutions.github.io/kupo/) for the new output: + +```sh +yacd topup my-net --address addr_test1... --lovelace 1000000 --await +``` + +`--await` requires a Kupo URL. Pass `--kupo-url`, or run under `yacd run`, which +sets `YACD_KUPO_URL` automatically: + +```sh +yacd run my-net -- sh -c \ + 'yacd topup my-net --address "$ADDR" --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL" --await' +``` + +The loopback faucet URL exposed by `run` is exempt from the trust gate, so no +`--trust-faucet-url` is needed there. When the output appears, `topup` prints +`Confirmed on-chain.` and exits. See +[Host access and the `YACD_*` contract](connecting-tools.md) for how `run` +bridges the cluster-internal endpoints to your host. + +## See also + +- [CLI reference](../reference/cli.md) — every `topup` flag and default. +- [CardanoNetwork reference](../reference/cardanonetwork.md) — faucet and wallet + spec fields and defaults. +- [Security](../concepts/security.md) — the faucet trust gate and host-only + token rationale. diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md new file mode 100644 index 00000000..225e38b1 --- /dev/null +++ b/docs/developer/getting-started.md @@ -0,0 +1,164 @@ +# Getting started + +This tutorial takes you from nothing to a running local Cardano devnet and a +funded address in a handful of commands. You will install the `yacd` CLI, bring +up a devnet, inspect it, query the chain tip, fund an address, and tear it all +down again. + +By the end you will have run a complete local development loop. For *why* the +pieces fit together this way, see +[Architecture](../concepts/architecture.md). + +!!! note "What you need" + A working [Docker](https://www.docker.com/) (or compatible container + runtime) so the CLI can create a local [k3d](https://k3d.io) cluster. + Everything else, including k3d itself, is provisioned for you. + +## 1. Install the CLI + +Install the `yacd` binary using one of the options below. + +=== "Download a release" + + Download the binary for your platform from the project's GitHub Releases + page, make it executable, and move it onto your `PATH`: + + ```sh + chmod +x yacd + sudo mv yacd /usr/local/bin/yacd + ``` + +=== "Build from source" + + From a clone of the repository, build the CLI with the Go toolchain: + + ```sh + go build -o yacd ./cli/cmd/yacd + sudo mv yacd /usr/local/bin/yacd + ``` + +Verify the install: + +```sh +yacd --version +``` + +You should see a version line printed to your terminal. If the command is not +found, confirm the directory you moved `yacd` into is on your `PATH`. + +## 2. Bring up the devnet + +Create the cluster, install the operator, and apply a default funded network +with a single command: + +```sh +yacd devnet +``` + +This provisions a managed k3d cluster, installs the YACD operator into it, and +applies one network named `devnet` in the `devnet` namespace. Progress streams +to your terminal while it works, and on success it prints a summary like this: + +```text +devnet is ready. + Cluster: (context ) + Operator: + Ogmios: + Kupo: + Wallet: + +Try: + yacd exec devnet -- cardano-cli query tip --testnet-magic 42 + yacd devnet down +``` + +The `Wallet` line is a pre-funded developer address, and the network uses +network magic `42`. Note both: you will use them in the steps below. The two +commands under `Try:` are exactly the next two things you will run. + +For what the cluster, operator, and network are and how they relate, see +[Architecture](../concepts/architecture.md). + +## 3. List and inspect the network + +List the networks in the cluster. Because `devnet` runs in its own `devnet` +namespace, list across all namespaces with `-A`: + +```sh +yacd list -A +``` + +The `devnet` network you just created appears in the output. Inspect it in +detail, including its status and connection information: + +```sh +yacd info devnet +``` + +`yacd info` prints the network's readiness and the endpoint URLs your tools +connect to. Keep this command handy while you work; it is the quickest way to +check what a network is exposing. + +## 4. Query the chain tip + +Run `cardano-cli` *inside* the node Pod to query the current chain tip. This is +the first command from the `Try:` hint: + +```sh +yacd exec devnet -- cardano-cli query tip --testnet-magic 42 +``` + +`yacd exec` runs the command directly inside the primary node Pod, where the +node socket and the `YACD_*` environment are already set, so `cardano-cli` finds +the socket automatically. The `--testnet-magic 42` value is the devnet's network +magic from step 2. + +The output is a JSON object describing the tip (slot, block, epoch, and sync +percentage). Run it again after a few seconds and the slot advances, confirming +the chain is producing blocks. + +## 5. Fund an address + +The faucet runs inside the cluster, so reach it through `yacd run`, which +forwards the faucet to a local port and exposes it as `$YACD_FAUCET_URL`. +Replace `
` with a Cardano testnet address you control (the `Wallet` +address from step 2 works), and `` with the amount to send +(1 ADA = 1,000,000 lovelace): + +```sh +yacd run devnet -- sh -c \ + 'yacd topup devnet --address
--lovelace --faucet-url "$YACD_FAUCET_URL"' +``` + +Both `--address` and `--lovelace` are required, and `--lovelace` must be greater +than zero. On success the faucet submits a funding transaction and `topup` +prints the transaction ID. The loopback faucet URL that `run` exposes is exempt +from the [trust gate](../concepts/security.md), so no extra flags are needed. + +!!! warning "The faucet is local-only" + The faucet and its auth token are part of your local devnet only. Never + point `yacd topup` at a shared or public network, and never send the faucet + token off your machine. + +For the full funding workflow, including waiting for on-chain confirmation, see +the [funding guide](funding.md). + +## 6. Tear it down + +When you are finished, delete the managed cluster: + +```sh +yacd devnet down +``` + +This removes the k3d cluster and everything in it. Your next `yacd devnet` will +start fresh. + +## Where to go next + +- [Working with networks](networks.md) — apply your own network definitions. +- [Connecting tools](connecting-tools.md) — wire Ogmios, Kupo, and other tools + to a network from your host. +- [Funding](funding.md) — the full faucet and top-up workflow. +- [CLI reference](../reference/cli.md) — every command, flag, and default. +- [Architecture](../concepts/architecture.md) — why YACD is built this way. diff --git a/docs/developer/networks.md b/docs/developer/networks.md new file mode 100644 index 00000000..205ae99f --- /dev/null +++ b/docs/developer/networks.md @@ -0,0 +1,146 @@ +# Defining networks + +This page collects task recipes for the network lifecycle: write an Environment +file, apply it, validate it without applying, inspect what is running, and tear +it down. Each `yacd` command keys off a `NAME` argument; the `NAME` becomes the +`CardanoNetwork` name and, unless you pass `--namespace`, the namespace as well, +so each environment lands in its own isolated namespace by default. + +For what each Environment field means, see the +[Environment file reference](../reference/environment.md) and the +[CardanoNetwork reference](../reference/cardanonetwork.md). For complete, +copy-paste manifests, see [Recipes](../recipes.md). For every command flag and +default, see the [CLI reference](../reference/cli.md). + +## Write a minimal Environment and apply it + +An Environment file carries the `apiVersion`/`kind` envelope and a single +`spec.network`. The identity (name and namespace) is supplied on the command +line, not in the file. + +Save the single-pool local manifest from [Recipes](../recipes.md) as +`yacd.yaml`, then apply it: + +```sh +yacd up dev -f yacd.yaml +``` + +`yacd up` renders the Environment into a `CardanoNetwork` named `dev` in +namespace `dev`, creates the namespace if needed, server-side-applies the +network, and then waits until it reports `Ready`. Use `--wait=false` to apply +without blocking, and `--timeout` to change the readiness deadline (default +`12m`). See the [CLI reference](../reference/cli.md) for the full flag set. + +!!! note + `spec.network.mode`, `spec.network.node.version`, and + `spec.network.node.port` must be written explicitly in the file; the loader + rejects a document that omits them. Local mode additionally requires + `spec.network.local` (with `networkMagic`, `era`, `timing.slotLength`, + `timing.epochLength`, and `topology.pools.count`). See the + [Environment file reference](../reference/environment.md) for the required + fields per mode. + +## Validate without applying + +Use `--dry-run` to render the manifest and print it to stdout without touching +the cluster. This runs the same Environment validation and rendering as a real +apply, so it catches envelope, field, and mode errors: + +```sh +yacd up dev -f yacd.yaml --dry-run +``` + +The rendered `CardanoNetwork` is written to stdout; a `Dry run: rendered +CardanoNetwork dev/dev; no resources applied.` line is written to stderr. No +namespace is created and nothing is applied. + +## Inspect running environments + +List the `CardanoNetwork` objects in the active namespace: + +```sh +yacd list +``` + +The table shows `NAME`, `NAMESPACE`, `MODE`, `READY`, and a comma-separated +`ENDPOINTS` summary (for example `ogmios,kupo,faucet`, or `-` when nothing is +published yet). `READY` reflects a fresh `Ready` condition, so a stale status is +reported as not ready. + +List across every namespace with `-A`, or emit machine-readable JSON with +`--json`: + +```sh +yacd list -A +yacd list --json +``` + +Print full status and connection details for one environment: + +```sh +yacd info dev +``` + +`yacd info` shows the network mode and magic, published endpoint URLs, faucet +and wallet status, and the `metav1.Condition` list. Add `--json` for a stable, +scriptable projection: + +```sh +yacd info dev --json +``` + +## Tear down + +Delete an environment and wait for it and its garbage-collected children to be +removed: + +```sh +yacd down dev +``` + +Deletion is idempotent: a network that is already absent is reported as success. +Use `--wait=false` to issue the delete without blocking, and `--timeout` to +change the removal deadline (default `5m`). + +## Run a public profile locally + +Public mode connects a node to a real Cardano network instead of a synthetic +local chain. The `preview` and `preprod` test networks need no bootstrap and are +the recommended way to develop against public-network data. + +Save the preview manifest from [Recipes](../recipes.md) as `yacd.yaml`, then +apply it the same way: + +```sh +yacd up preview -f yacd.yaml +``` + +Switch to preprod by setting `spec.network.public.profile: preprod`. For both +profiles, `spec.network.public.bootstrap` must be absent; it is accepted only +for `mainnet`. Public profiles sync from the network, so allow extra time and +storage compared with a local network. The copy-paste preview and preprod +manifests live in [Recipes](../recipes.md). + +## Run mainnet + +!!! warning + Mainnet is heavyweight and gated. A mainnet Environment **must** set + `spec.network.public.bootstrap.mithril` (the loader rejects a mainnet + document without it), provisions large persistent volumes, and bootstraps + chain state from [Mithril](https://mithril.network). `yacd up` refuses to + apply a mainnet network unless you pass `--allow-mainnet`; without it the + command fails with an explicit error. A `--dry-run` still renders a mainnet + manifest without the flag, but prints a warning and applies nothing. + +Save the mainnet manifest from [Recipes](../recipes.md) as `yacd.yaml`, then +apply it with the gate flag: + +```sh +yacd up mainnet -f yacd.yaml --allow-mainnet +``` + +Because the chain bootstrap and sync take far longer than a local or test +network, raise `--timeout` accordingly. See the +[CardanoNetwork reference](../reference/cardanonetwork.md) for the +`spec.public.bootstrap.mithril` fields and the +[Recipes](../recipes.md) page for the full mainnet manifest. diff --git a/docs/host-access.md b/docs/host-access.md deleted file mode 100644 index 071a7a3d..00000000 --- a/docs/host-access.md +++ /dev/null @@ -1,135 +0,0 @@ -# Host access and the `YACD_*` contract - -The operator publishes a network's chain APIs as **cluster-internal** -`*.svc.cluster.local` Service URLs, which a laptop or CI runner cannot reach. -The `run`, `connect`, and `exec` verbs bridge that gap, and the `YACD_*` -environment variables are the **integration surface**: your tests read ordinary -env vars and never parse a YACD file, so the test runner stays YACD-agnostic and -the same test works locally and in CI. - -This page is a reference for that contract. It assumes a network is already -`up` and `Ready`. - -## The verbs - -| Verb | What it does | -|---|---| -| `yacd run NAME -- ` | Establish scoped port-forwards to the chain APIs, inject the `YACD_*` environment, run `` on the host, and tear the forwards down when it exits. No command drops into `$SHELL`. The command's exit code is propagated. This is the primary test/CI path. | -| `yacd connect NAME` | Hold the forwards open in the foreground (one terminal) while you work in another, writing the loopback URLs to `.yacd//endpoints.json`. Re-establishes dropped forwards; runs until Ctrl-C. | -| `yacd exec NAME -- ` | Run `` **inside** the primary node Pod, for tools that reach the node over its local Unix socket. | -| `yacd topup NAME --await …` | Fund an address through the faucet and wait for the funding transaction to be confirmed on-chain. | - -## `run` vs `exec`: which one? - -- Use **`run`** for tooling that speaks to **Ogmios, Kupo, or the faucet over - TCP** — it forwards those Services to loopback ports and points `YACD_*` at - them. -- Use **`exec`** for **`cardano-cli` and anything that needs the node's Unix - domain socket**. `cardano-cli` talks to the node over a local socket - (`--socket-path`), not TCP, so a port-forward cannot expose it; `exec` runs - the command in the Pod where the socket is local and sets - `CARDANO_NODE_SOCKET_PATH`. - -`exec` runs the command directly, **not** through a shell, so `$VAR` references -in arguments are not expanded. `cardano-cli` reads `CARDANO_NODE_SOCKET_PATH` -from the environment automatically: - -```sh -yacd exec my-net -- cardano-cli query tip --testnet-magic 42 -``` - -To interpolate `YACD_*` variables into arguments, run a shell explicitly: - -```sh -yacd exec my-net -- sh -c 'cardano-cli query tip --testnet-magic "$YACD_NETWORK_MAGIC"' -``` - -When stdin and stdout are a terminal, `exec` attaches an interactive TTY (raw -mode, with window resizes forwarded), so this opens an interactive shell inside -the node Pod; piped or non-terminal invocations (for example in CI) stream -without a TTY: - -```sh -yacd exec my-net -- sh -``` - -## The `YACD_*` environment variables - -The variable **names are identical** whether a command runs on the host -(`run`/`connect`) or inside the Pod (`exec`); only the values adapt, which is -what makes a test transparent to where it runs. This is contract **version 1**: -adding a variable is backward compatible; renaming or removing one is a breaking -change. - -| Variable | Host (`run`/`connect`) | In-pod (`exec`) | Present when | -|---|---|---|---| -| `YACD_NETWORK` | network name | same | always | -| `YACD_NAMESPACE` | namespace | same | always | -| `YACD_NETWORK_MAGIC` | e.g. `42` | same | the controller publishes a network magic | -| `YACD_OGMIOS_URL` | `ws://127.0.0.1:` | `ws://..svc.cluster.local:` | Ogmios published | -| `YACD_KUPO_URL` | `http://127.0.0.1:` | `http://..svc.cluster.local:` | Kupo published | -| `YACD_FAUCET_URL` | `http://127.0.0.1:` | `http://..svc.cluster.local:` | faucet published | -| `YACD_FAUCET_TOKEN` | faucet auth token | *(omitted)* | faucet ready, **host only** | -| `CARDANO_NODE_SOCKET_PATH` | — | `/ipc/node.socket` | `exec` only | - -The host URL schemes are taken from what the operator published (Ogmios stays -`ws://`), with the host rewritten to a random loopback port. `YACD_FAUCET_TOKEN` -is deliberately **never** set in the in-pod (`exec`) environment: a Bearer token -in the exec command line would leak into apiserver audit logs, and in-pod -tooling does not need it. - -## `connect` and endpoint state files - -`connect` writes the loopback URLs to a gitignored endpoint state file for -other host processes (a dApp dev server, a REPL, repeated IDE test runs) to -read. When `--namespace` is not set and the namespace defaults to the network -name, the path stays: - -```text -.yacd//endpoints.json -``` - -When `--namespace` is set, the path includes both identity parts so networks -with the same name in different namespaces do not collide: - -```text -.yacd///endpoints.json -``` - -The document shape is the same in either location: - -```json -{ - "network": "my-net", - "namespace": "my-net", - "networkMagic": 42, - "ogmiosUrl": "ws://127.0.0.1:34521", - "kupoUrl": "http://127.0.0.1:34522", - "faucetUrl": "http://127.0.0.1:34523" -} -``` - -The file is created `0600` under `0700` state directories and **never contains -the faucet token**. Its ports are only live while `connect` is running. A clean -disconnect removes the file, and a dropped forward removes the stale file before -re-establishing, reassigning local ports, and writing a fresh document. - -## Funding with `topup --await` - -`yacd topup NAME --address ADDR --lovelace N --await` funds `ADDR` through the -faucet and then polls Kupo until the funding transaction's output appears, so a -test never races chain inclusion. `--await` requires a Kupo URL: pass -`--kupo-url`, or run under `yacd run` (which sets `YACD_KUPO_URL`): - -```sh -yacd run my-net -- sh -c \ - 'yacd topup my-net --address "$ADDR" --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL" --await' -``` - -The loopback faucet URL is exempt from the `topup` trust gate, so no -`--trust-faucet-url` flag is needed against a `run`/`connect` forward. - -## See also - -- The full operator and CLI surface in the [README](../README.md). -- The architecture direction in [DESIGN.md](../DESIGN.md). diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..ee511315 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,54 @@ +# yacd + +yacd is a Kubernetes-native manager for Cardano development environments. It is +aimed at people building on [Cardano](https://developers.cardano.org), not +validators, stake pool operators, or production network operators. + +yacd ships as two surfaces: + +- A **Kubernetes operator** that owns declarative cluster state. The + `CardanoNetwork` CRD reconciles a Cardano node with [Ogmios](https://ogmios.dev) + as the default chain API, [Kupo](https://cardanosolutions.github.io/kupo/) as + the default chain index, and an opt-in token-protected faucet for local + top-ups. +- A companion **`yacd` CLI** that owns local developer workflow: standing up a + devnet, applying a checked-in config, waiting for readiness, printing + connection details, forwarding endpoints to your host, and funding wallets. + +## Where to go next + +=== "Developing locally" + + Build and test against a Cardano network on your own machine. Start with the + [Getting started tutorial](developer/getting-started.md). + +=== "Running on a cluster" + + Install the operator into an existing Kubernetes cluster so a team can share + YACD-managed networks. See [Operator installation](operator/installation.md). + +## Try it in one command + +A single `yacd devnet` provisions a local cluster, installs the operator, and +applies a funded network: + +```sh +yacd devnet +``` + +Follow the [Getting started tutorial](developer/getting-started.md) for the full +path, including how to inspect the network and wire it into your tests. Every +flag and default is listed in the [CLI reference](reference/cli.md). + +## External tools + +yacd composes existing Cardano and Kubernetes tooling. These links go to the +upstream projects: + +- [Cardano developer portal](https://developers.cardano.org) +- [Ogmios](https://ogmios.dev) — chain API +- [Kupo](https://cardanosolutions.github.io/kupo/) — chain index +- [cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync) — relational chain index +- [Mithril](https://mithril.network) — fast bootstrap for public networks +- [Helm](https://helm.sh) and [k3d](https://k3d.io) — packaging and the local cluster +- [Kubernetes](https://kubernetes.io) diff --git a/docs/operator/db-sync.md b/docs/operator/db-sync.md new file mode 100644 index 00000000..c2a1e6d9 --- /dev/null +++ b/docs/operator/db-sync.md @@ -0,0 +1,162 @@ +# DB-Sync indexing + +Index an existing `CardanoNetwork` with [cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync) +by applying a `CardanoDBSync` in the **same namespace**. db-sync follows the +chain through a node socket and writes a queryable Postgres schema. + +This guide covers the two decisions you make before applying: where the Postgres +database comes from, and where db-sync runs relative to the network. For the full +spec and every `insert` option, see the +[CardanoDBSync reference](../reference/cardanodbsync.md). For the rationale behind +the placement and insert trade-offs, see +[Architecture](../concepts/architecture.md). + +## Before you start + +- A `CardanoNetwork` already exists and is `Ready` in the namespace you will use. + See [Networks](../developer/networks.md) and the + [yacd CLI reference](../reference/cli.md). +- You can apply manifests to that namespace with `kubectl`. + +`CardanoDBSync` requires only `networkRef.name` and a `database` mode; every other +field has a default. The examples below use the `devnet` network in the +`yacd-smoke` namespace. + +## Decision 1: choose the Postgres source + +`spec.database` requires **exactly one** of `managed` or `external`. + +### Managed Postgres (recommended for local development) + +Set `database.managed` and the controller provisions Postgres for you. When you do +not supply credentials, it creates an owned Secret holding the generated password +and reports the name in `status.database.authSecretName`. + +```yaml +spec: + networkRef: + name: devnet + database: + managed: {} +``` + +`managed: {}` accepts all defaults (image `postgres:17.2-alpine`, database +`cexplorer`, user `postgres`). To reuse a password you already hold, point +`managed.authSecretRef.name` at a same-namespace Secret with the password under +the key `password`. See the +[CardanoDBSync reference](../reference/cardanodbsync.md) for the full `managed` +surface. + +### External Postgres + +Set `database.external` to point db-sync at a Postgres instance you operate. You +must supply the password through `passwordSecretRef`, which references a +same-namespace Secret. + +Create the Secret first: + +```sh +kubectl -n yacd-smoke create secret generic dbsync-postgres \ + --from-literal=password=change-me +``` + +Then reference it from the `CardanoDBSync`: + +```yaml +spec: + networkRef: + name: devnet + database: + external: + host: postgres.yacd-smoke.svc.cluster.local + passwordSecretRef: + name: dbsync-postgres +``` + +`passwordSecretRef.key` defaults to `password`, so a Secret with that key needs no +`key` field. `port` (`5432`), `database` (`cexplorer`), `user` (`postgres`), and +`sslMode` (`disable`) all default; override them in `external` when your server +differs. See the +[CardanoDBSync reference](../reference/cardanodbsync.md) for the full `external` +surface, including `sslMode` values. + +## Decision 2: choose where db-sync runs + +`spec.placement.mode` selects how db-sync reaches the node socket. + +- `dedicatedFollower` (default) runs a separate follower node colocated with + db-sync, owned by the `CardanoDBSync` controller. Omitting `placement` keeps this + behavior. +- `primarySidecar` injects db-sync into the referenced network's primary node Pod + instead of running a separate follower. + +```yaml +spec: + placement: + mode: primarySidecar +``` + +`followerNode` cannot be set when `placement.mode` is `primarySidecar`; the spec +rejects that combination. For when to prefer each mode, see +[Architecture](../concepts/architecture.md). + +!!! warning "Placement is fixed for a database" + The placement mode is recorded against the db-sync state in + `status.database.acceptedPlacementMode`. Changing it later requires deleting + and recreating the `CardanoDBSync` with a fresh or compatible database. + +## Apply the CardanoDBSync + +Use one of the manifests from [Recipes](../recipes.md) (mirrored from `examples/`), +or write your own from the decisions above. Apply it into the network's namespace: + +```sh +kubectl apply -f cardanodbsync.yaml +``` + +## Check Ready and Synced + +`CardanoDBSync` has no dedicated `yacd` subcommand; check it with `kubectl`. The +printed columns surface progress at a glance: + +```sh +kubectl -n yacd-smoke get cardanodbsync +``` + +```text +NAME NETWORK READY SYNCED AGE +dbsync devnet True True 5m +``` + +- `READY` (`Ready` condition) reports that db-sync is usable through its published + database endpoint. +- `SYNCED` (`Synced` condition) reports that db-sync has caught up to the node tip. + +Wait for both conditions to read `True`: + +```sh +kubectl -n yacd-smoke wait --for=condition=Ready cardanodbsync/dbsync --timeout=15m +kubectl -n yacd-smoke wait --for=condition=Synced cardanodbsync/dbsync --timeout=60m +``` + +For more detail, inspect the resource status. It reports the Postgres endpoint, +indexing progress (block heights and lag), and, for managed Postgres without your +own Secret, the generated credentials Secret name: + +```sh +kubectl -n yacd-smoke get cardanodbsync/dbsync -o yaml +``` + +The Postgres endpoint and metrics endpoint appear under `status.endpoints`. For +the complete status surface, including every condition type, see the +[CardanoDBSync reference](../reference/cardanodbsync.md). + +## Next steps + +- Tune what db-sync writes with `spec.config.insert`; see the + [CardanoDBSync reference](../reference/cardanodbsync.md). +- Understand the placement and insert trade-offs in + [Architecture](../concepts/architecture.md). +- Read the upstream + [cardano-db-sync documentation](https://github.com/IntersectMBO/cardano-db-sync) + for schema details and query guidance. diff --git a/docs/operator/installation.md b/docs/operator/installation.md new file mode 100644 index 00000000..43a0cefb --- /dev/null +++ b/docs/operator/installation.md @@ -0,0 +1,112 @@ +# Installation + +Install the YACD operator on a remote Kubernetes cluster with [Helm](https://helm.sh) from the OCI chart published to GitHub Container Registry. The chart deploys the controller manager, its RBAC and ServiceAccount, a secured metrics Service, and the `CardanoNetwork` and `CardanoDBSync` CRDs. + +This page covers a remote install. For a local k3d cluster, the `yacd` CLI manages its own cluster lifecycle; see the [CLI reference](../reference/cli.md). For every chart value and manager flag, see the [configuration reference](../reference/configuration.md). + +## Prerequisites + +- A Kubernetes cluster, version 1.29 or later (the chart sets `kubeVersion: ">= 1.29.0-0"`). +- Helm 3.8 or later, which can pull OCI charts. +- `kubectl` configured against the target cluster. + +## Install + +Install the chart into a namespace, creating the namespace if it does not exist. Replace `` with the release version you want (for example `0.1.1`) and `` with your target namespace. + +```sh +helm install yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace \ + --create-namespace +``` + +The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.1.1` ships `appVersion` `v0.1.1`). To discover the chart metadata before installing, run: + +```sh +helm show chart oci://ghcr.io/meigma/yacd/chart --version +``` + +The chart bundles the CRDs under `charts/yacd/crds/`, so Helm installs `cardanonetworks.yacd.meigma.io` and `cardanodbsyncs.yacd.meigma.io` as part of the release. You do not apply CRDs separately. + +To override any value, pass `--set` or `-f values.yaml`. The full value surface (image, metrics, RBAC, leader election, manager logging, Kyverno, security context, scheduling) lives in the [configuration reference](../reference/configuration.md). + +## Verify + +Confirm the manager Deployment becomes Available: + +```sh +kubectl rollout status deployment/yacd-controller-manager --namespace +``` + +Confirm the CRDs are registered: + +```sh +kubectl get crd cardanonetworks.yacd.meigma.io cardanodbsyncs.yacd.meigma.io +``` + +Operators drive networks with the same `yacd` verbs documented in the [CLI reference](../reference/cli.md). Once the manager is Available you can apply a `CardanoNetwork` or `CardanoDBSync`; see the copy-paste manifests in [Recipes](../recipes.md). + +## Upgrading + +Upgrade to a new chart version in place: + +```sh +helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace +``` + +!!! warning "Helm does not upgrade bundled CRDs" + The CRDs ship under the chart's `crds/` directory. Helm installs those CRDs on first install but **never upgrades or deletes them** on `helm upgrade` or `helm uninstall`. When a release changes a CRD schema, apply the updated definitions yourself before or after the upgrade: + + ```sh + kubectl apply -f https://raw.githubusercontent.com/meigma/yacd/v/charts/yacd/crds/yacd.meigma.io_cardanonetworks.yaml + kubectl apply -f https://raw.githubusercontent.com/meigma/yacd/v/charts/yacd/crds/yacd.meigma.io_cardanodbsyncs.yaml + ``` + + Review the release notes for the target version to confirm whether a CRD changed before applying. + +## Metrics + +The chart ships a secured metrics Service by default. The manager serves metrics over HTTPS (`--metrics-secure` defaults to true) behind the Kubernetes authn/authz filter, so scrapers must authenticate and carry RBAC to read the endpoint. The chart also creates a `ClusterRole` that grants the metrics-reader permission for that path. + +The Service is a `ClusterIP` named `yacd-controller-manager-metrics-service`, exposing port `8443` by default. To scrape it, grant your scraper the metrics-reader role and target the Service over HTTPS. To turn metrics off or change the port and TLS material, see the `metrics.*` values in the [configuration reference](../reference/configuration.md). + +## Supply-chain image verification (optional) + +The release workflow attests the manager image, faucet image, and Helm chart with GitHub-native (Sigstore keyless) attestations. The chart includes an opt-in [Kyverno](https://kyverno.io) `ClusterPolicy` that verifies those attestations on admission. It is disabled by default (`kyverno.imageVerification.enabled: false`). + +Enabling it requires a running Kyverno installation in the cluster. When enabled with no explicit `imageReferences`, the policy verifies the configured manager and faucet image repositories, requiring a keyless Sigstore attestation from the `meigma/yacd` release workflow. To enable it: + +```sh +helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace \ + --set kyverno.imageVerification.enabled=true +``` + +The attestor issuer, subject pattern, Rekor URL, validation failure action, and image references are all configurable; see the `kyverno.imageVerification.*` values in the [configuration reference](../reference/configuration.md). To verify a pulled chart or image directly with the GitHub CLI instead of at admission time, use `gh attestation verify`. + +## Uninstall + +Remove the release: + +```sh +helm uninstall yacd --namespace +``` + +!!! warning "CRDs and custom resources are not removed" + `helm uninstall` does not delete the CRDs installed from `crds/`, and it does not delete any `CardanoNetwork` or `CardanoDBSync` resources you created. Delete those resources first if you want the operator to tear down their owned runtime children, then remove the CRDs manually once nothing depends on them: + + ```sh + kubectl delete cardanonetworks --all --all-namespaces + kubectl delete cardanodbsyncs --all --all-namespaces + kubectl delete crd cardanonetworks.yacd.meigma.io cardanodbsyncs.yacd.meigma.io + ``` + +To remove the namespace if you created it solely for YACD: + +```sh +kubectl delete namespace +``` diff --git a/docs/operator/testing.md b/docs/operator/testing.md new file mode 100644 index 00000000..fa7bd3d3 --- /dev/null +++ b/docs/operator/testing.md @@ -0,0 +1,111 @@ +# Testing & CI + +Use YACD to give an automated test suite a real, ephemeral Cardano network and +tear it down deterministically afterward. The pattern is three commands: + +```sh +yacd up my-net -f env.yaml --wait # create, block until Ready +yacd run my-net -- # run tests with YACD_* wired in +yacd down my-net --wait # delete, block until gone +``` + +Your test runner stays YACD-agnostic: it reads the `YACD_*` environment +variables `run` injects and never parses a YACD file, so the same suite works on +a laptop and in CI. For the meaning of those variables and the `run`/`exec` +semantics, see [Host access and the `YACD_*` contract](../developer/connecting-tools.md); +for every flag and default, see the [CLI reference](../reference/cli.md). + +## 1. Bring the network up, gated on Ready + +`yacd up` server-side-applies the `CardanoNetwork` rendered from your +environment file and, with `--wait` (the default), blocks until the network +reports Ready before returning a zero exit code. A non-zero exit means the +network never became Ready, so a CI step can depend on `up` succeeding: + +```sh +yacd up my-net -f env.yaml --wait +``` + +`--wait` is on by default; pass `--wait=false` to apply without blocking. The +readiness deadline is `--timeout` (default 12m). Tune it for slow runners or +larger profiles; raising it does not change what "Ready" means, only how long +`up` will wait for it. See the [CLI reference](../reference/cli.md) for the full +flag set. + +## 2. Run the suite under `yacd run` + +`yacd run NAME -- ` establishes scoped port-forwards to the network's +chain APIs, exports the `YACD_*` environment into the command, runs it on the +host, and tears the forwards down when it exits. The command's exit status is +propagated to your shell, so the test runner's pass/fail code is what CI sees: + +```sh +yacd run my-net -- go test ./e2e/... +``` + +Always put `--` before the test command so its own flags are passed through to +it rather than parsed by `yacd`. Inside the command, the runner reads endpoints +from `YACD_OGMIOS_URL`, `YACD_KUPO_URL`, and `YACD_FAUCET_URL` (loopback URLs on +the host) plus `YACD_FAUCET_TOKEN` for the host-only faucet. The variable names +and the loopback/faucet-token details are documented once in +[Host access and the `YACD_*` contract](../developer/connecting-tools.md). + +!!! warning "Use `exec`, not `run`, for `cardano-cli`" + `run` forwards Ogmios, Kupo, and the faucet over TCP. Tools that reach the + node over its local Unix socket (notably `cardano-cli`) need + `yacd exec NAME -- …` instead, which runs in the node Pod and sets + `CARDANO_NODE_SOCKET_PATH`. See + [Choose a verb](../developer/connecting-tools.md#choose-a-verb). + +## 3. Tear down deterministically + +`yacd down` deletes the `CardanoNetwork` and, with `--wait` (the default), +blocks until the object and its garbage-collected children are gone before +returning. Deletion is idempotent: a network that is already absent is reported +as success, so teardown is safe to run unconditionally in a cleanup step. + +```sh +yacd down my-net --wait +``` + +The removal deadline is `--timeout` (default 5m). Run teardown even when the +test step failed, so a broken run never leaks a cluster network. + +## Minimal CI snippet + +The three steps map directly onto a CI job. Bring the network up, run the suite, +and tear down whether or not the suite passed. + +```yaml +steps: + - name: Bring up the network + run: yacd up my-net -f env.yaml --wait + + - name: Run the test suite + run: yacd run my-net -- go test ./e2e/... + + - name: Tear down the network + if: always() + run: yacd down my-net --wait +``` + +`if: always()` (or your CI's equivalent) guarantees teardown runs after a +failed test step. Because `down` is idempotent, the cleanup step is also safe to +keep even if `up` never created the network. + +## Selecting the cluster and namespace + +`up`, `run`, and `down` all accept the global `--kubeconfig`, `--context`, and +`-n/--namespace` flags, so a CI runner can target a cluster without editing the +test command. These globals are documented once in the +[CLI reference](../reference/cli.md). + +When `--namespace` is not set, the namespace defaults to the network name, so +`yacd up my-net …` applies the network into the `my-net` namespace and `up` +auto-creates that namespace if it does not exist. + +!!! warning "Mainnet is not a CI target" + `yacd up` refuses to apply a mainnet network without `--allow-mainnet`, + because mainnet creates large persistent volumes and bootstraps from + Mithril. Automated tests should use a local devnet or a public testnet + profile, not mainnet. diff --git a/docs/recipes.md b/docs/recipes.md new file mode 100644 index 00000000..6ac8d7dd --- /dev/null +++ b/docs/recipes.md @@ -0,0 +1,251 @@ +# Recipes + +Copy-paste manifests for the most common YACD setups. Each recipe is mirrored +verbatim from a file under `examples/`. Use them as a starting point and adjust +field values to suit your cluster. + +Developer environment files (`kind: Environment`) are applied with the CLI: + +```sh +yacd up dev -f yacd.yaml +``` + +DB-Sync resources (`kind: CardanoDBSync`) are applied with `kubectl`: + +```sh +kubectl apply -f cardanodbsync.yaml +``` + +For the full field semantics, defaults, and enums behind these manifests, see +the reference pages linked under each section. For every CLI flag and default, +see [CLI reference](reference/cli.md). + +## Local networks + +A local network runs a self-contained devnet with a fast block schedule, an +optional faucet, and an optional pre-funded developer wallet. See the +[Environment file reference](reference/environment.md) for every field. + +### Single-pool local devnet + +Use when you want an isolated, fast local Cardano network with a funded +developer wallet and a faucet for top-ups. + +```yaml +apiVersion: yacd.meigma.io/devconfig/v1alpha1 +kind: Environment +spec: + network: + mode: local + node: + version: "11.0.1" + port: 3001 + storage: + size: 2Gi + chainAPI: + faucet: + enabled: true + port: 8080 + defaultSource: utxo1 + minTopUpLovelace: 1000000 + # Raised so the operator can fund the developer wallet (100,000 ADA) + # through the faucet within the per-request maximum. + maxTopUpLovelace: 100000000000 + wallet: + enabled: true + fundingLovelace: 100000000000 + local: + networkMagic: 42 + era: conway + timing: + slotLength: 100ms + epochLength: 500 + topology: + pools: + count: 1 +``` + +!!! warning "The faucet is local-only" + The faucet and the pre-funded developer wallet exist only on local + networks. They are never provisioned for public profiles. + +## Public networks + +A public network syncs a node against an upstream Cardano network. Select the +target with `spec.network.public.profile`. See the +[Environment file reference](reference/environment.md) for every field. + +### Preview testnet + +Use when you want to develop against the public Preview testnet. + +```yaml +apiVersion: yacd.meigma.io/devconfig/v1alpha1 +kind: Environment +spec: + network: + mode: public + node: + version: "11.0.1" + port: 3001 + storage: + size: 20Gi + public: + profile: preview +``` + +### Preprod testnet + +Use when you want to develop against the public Preprod testnet. + +```yaml +apiVersion: yacd.meigma.io/devconfig/v1alpha1 +kind: Environment +spec: + network: + mode: public + node: + version: "11.0.1" + port: 3001 + storage: + size: 20Gi + public: + profile: preprod +``` + +### Mainnet + +Use when you need to sync against Cardano mainnet. + +```yaml +apiVersion: yacd.meigma.io/devconfig/v1alpha1 +kind: Environment +spec: + network: + mode: public + node: + version: "11.0.1" + port: 3001 + public: + profile: mainnet + bootstrap: + mithril: {} +``` + +!!! warning "Mainnet has hard requirements" + - A [Mithril](https://mithril.network) bootstrap (`bootstrap.mithril`) is + required to seed the chain database; syncing mainnet from genesis is not + practical. + - Mainnet needs **large persistent storage** well beyond the 20Gi used for + testnets. Size `node.storage.size` for the full mainnet chain. + - The CLI refuses to apply a mainnet network unless you pass + `--allow-mainnet`. See [CLI reference](reference/cli.md). + +## DB-Sync + +[cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync) indexes a +network's chain into PostgreSQL. A `CardanoDBSync` references an existing +network through `spec.networkRef` and connects to either an operator-managed or +an external database. See the +[CardanoDBSync reference](reference/cardanodbsync.md) for every field. + +### Managed PostgreSQL + +Use when you want YACD to provision and own the PostgreSQL database for you. + +```yaml +apiVersion: yacd.meigma.io/v1alpha1 +kind: CardanoDBSync +metadata: + name: dbsync + namespace: yacd-smoke +spec: + networkRef: + name: devnet + database: + managed: {} +``` + +### External PostgreSQL + +Use when you connect DB-Sync to a PostgreSQL instance you manage yourself, +supplying the password through a `Secret`. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: dbsync-postgres + namespace: yacd-smoke +type: Opaque +stringData: + password: change-me +--- +apiVersion: yacd.meigma.io/v1alpha1 +kind: CardanoDBSync +metadata: + name: dbsync + namespace: yacd-smoke +spec: + networkRef: + name: devnet + database: + external: + host: postgres.yacd-smoke.svc.cluster.local + passwordSecretRef: + name: dbsync-postgres +``` + +### Preprod DB-Sync on a dedicated follower + +Use when you index a public Preprod network with a dedicated follower node and +a minimal insert preset to reduce write volume. + +```yaml +apiVersion: yacd.meigma.io/v1alpha1 +kind: CardanoDBSync +metadata: + name: preprod-dbsync + namespace: yacd-smoke +spec: + networkRef: + name: preprod-smoke + placement: + mode: dedicatedFollower + database: + managed: {} + config: + insert: + preset: disable_all +``` + +### Preview DB-Sync as a primary sidecar + +Use when you co-locate DB-Sync with the primary node as a sidecar and tune its +runtime for a lightweight, low-overhead index. + +```yaml +apiVersion: yacd.meigma.io/v1alpha1 +kind: CardanoDBSync +metadata: + name: preview-dbsync-sidecar + namespace: yacd-smoke +spec: + networkRef: + name: preview-smoke + placement: + mode: primarySidecar + database: + managed: + parameters: + maxParallelMaintenanceWorkers: 0 + config: + ledgerBackend: inmemory + runtime: + cache: false + epochTable: false + forceIndexes: false + metricsPort: 8080 + insert: + preset: disable_all +``` diff --git a/docs/reference/cardanodbsync.md b/docs/reference/cardanodbsync.md new file mode 100644 index 00000000..32618653 --- /dev/null +++ b/docs/reference/cardanodbsync.md @@ -0,0 +1,329 @@ +# CardanoDBSync + +`CardanoDBSync` indexes a [`CardanoNetwork`](cardanonetwork.md) into a Postgres +database using [cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync). +This page is the dry, complete field reference for the custom resource. + +| Property | Value | +| --- | --- | +| API group / version | `yacd.meigma.io/v1alpha1` | +| Kind | `CardanoDBSync` | +| List kind | `CardanoDBSyncList` | +| Plural | `cardanodbsyncs` | +| Singular | `cardanodbsync` | +| Scope | Namespaced | +| Status subresource | enabled | + +For copy-paste manifests, see [Recipes](../recipes.md). The objects that +`CardanoDBSync` references (Secrets, the `CardanoNetwork`) must live in the same +namespace. + +## Print columns + +`kubectl get cardanodbsyncs` shows: + +| Column | Source | +| --- | --- | +| `Network` | `.spec.networkRef.name` | +| `Ready` | `.status.conditions[?(@.type=="Ready")].status` | +| `Synced` | `.status.conditions[?(@.type=="Synced")].status` | +| `Age` | `.metadata.creationTimestamp` | + +## Spec + +`spec` is required. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `networkRef` | object | yes | — | Same-namespace `CardanoNetwork` that db-sync indexes. See [networkRef](#networkref). | +| `image` | string | yes | `ghcr.io/intersectmbo/cardano-db-sync:13.7.1.0` | cardano-db-sync image reference. | +| `resources` | object | no | — | db-sync container resources (core `ResourceRequirements`). | +| `placement` | object | no | — | Where db-sync runs relative to the network. When omitted, the controller preserves the dedicated follower workload. See [placement](#placement). | +| `followerNode` | object | no | — | Dedicated follower node colocated with db-sync for local node socket access. See [followerNode](#followernode). | +| `database` | object | yes | — | Postgres database used by db-sync. Exactly one mode must be set. See [database](#database). | +| `stateStorage` | object | no | — | Persistent storage for db-sync ledger state. See [storage](#storage-spec). | +| `config` | object | no | — | Upstream db-sync behavior in Kubernetes-style field names. See [config](#config). | + +### Spec-level validation + +| Rule | Message | +| --- | --- | +| `!has(self.placement) \|\| self.placement.mode != 'primarySidecar' \|\| !has(self.followerNode)` | `followerNode cannot be set when placement.mode is primarySidecar` | + +### networkRef + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `name` | string | yes | — | Name of the referenced `CardanoNetwork`. Minimum length 1. | + +### placement + +| Field | Type | Required | Default | Enum | Description | +| --- | --- | --- | --- | --- | --- | +| `mode` | string | yes | `dedicatedFollower` | `dedicatedFollower`, `primarySidecar` | Whether db-sync uses a dedicated follower node or asks the referenced network's primary Pod to host it as a sidecar. | + +`dedicatedFollower` keeps the two-container workload with a colocated follower +node owned by the `CardanoDBSync` controller. `primarySidecar` requests db-sync +placement inside the referenced `CardanoNetwork` primary node Pod; `followerNode` +must not be set in this mode. + +### followerNode + +Configures the follower node owned by db-sync. Only meaningful in +`dedicatedFollower` placement. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `image` | string | no | derived from the referenced `CardanoNetwork` | Overrides the follower cardano-node image. | +| `storage` | object | no | — | Persistent follower node database storage. See [storage](#storage-spec). | +| `resources` | object | no | — | Follower node container resources (core `ResourceRequirements`). | + +### database + +Exactly one of `external` or `managed` must be set. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `external` | object | no | — | References a Postgres instance managed outside this resource. See [database.external](#databaseexternal). | +| `managed` | object | no | — | YACD-managed Postgres for local development. See [database.managed](#databasemanaged). | + +| Rule | Message | +| --- | --- | +| `has(self.external) != has(self.managed)` | `exactly one of database.external or database.managed must be set` | + +#### database.external + +| Field | Type | Required | Default | Enum | Description | +| --- | --- | --- | --- | --- | --- | +| `host` | string | yes | — | — | DNS name or IP address of the Postgres server. Minimum length 1. | +| `port` | int32 | yes | `5432` | — | Postgres server port. Range 1–65535. | +| `database` | string | yes | `cexplorer` | — | Postgres database name. | +| `user` | string | yes | `postgres` | — | Postgres user name. | +| `passwordSecretRef` | object | yes | — | — | Same-namespace Secret holding the Postgres password. See [secret key reference](#secret-key-reference). | +| `sslMode` | string | yes | `disable` | `disable`, `require`, `verify-ca`, `verify-full` | libpq `sslmode` used for Postgres connections. | + +#### database.managed + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `image` | string | yes | `postgres:17.2-alpine` | Postgres image reference. | +| `database` | string | yes | `cexplorer` | Postgres database name. | +| `user` | string | yes | `postgres` | Postgres user name. | +| `authSecretRef` | object | no | controller creates an owned Secret | Same-namespace Secret with the Postgres password in key `password`. When omitted, the controller creates a Secret and reports its name in `status.database.authSecretName`. See [secret reference](#secret-reference). | +| `storage` | object | no | — | Persistent Postgres data storage. See [storage](#storage-spec). | +| `parameters` | object | no | — | Basic Postgres startup parameters. See [database.managed.parameters](#databasemanagedparameters). | +| `resources` | object | no | — | Postgres container resources (core `ResourceRequirements`). | + +##### database.managed.parameters + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `maintenanceWorkMem` | quantity | no | — | Sets Postgres `maintenance_work_mem`. | +| `maxParallelMaintenanceWorkers` | int32 | no | — | Sets Postgres `max_parallel_maintenance_workers`. Minimum 0. | + +### storage (spec) + +The same `storage` shape is used by `spec.stateStorage`, +`spec.followerNode.storage`, and `spec.database.managed.storage`. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `size` | quantity | no | — | Requested persistent volume size. | +| `storageClassName` | string | no | — | Kubernetes StorageClass used for the PVC. | + +### Secret references + +#### secret reference + +Used by `database.managed.authSecretRef`. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `name` | string | yes | — | Name of the referenced Secret. Minimum length 1. | + +#### secret key reference + +Used by `database.external.passwordSecretRef`. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `name` | string | yes | — | Name of the referenced Secret. Minimum length 1. | +| `key` | string | yes | `password` | Secret data key containing the value. Minimum length 1. | + +## config + +Configures upstream db-sync behavior in Kubernetes-style field names. The +controller translates this object into the upstream db-sync configuration file. + +| Field | Type | Required | Default | Enum | Description | +| --- | --- | --- | --- | --- | --- | +| `runtime` | object | no | — | — | db-sync runtime flags outside `insert_options`. See [config.runtime](#configruntime). | +| `ledgerBackend` | string | yes | `lsm` | `inmemory`, `lsm` | How db-sync stores its ledger UTxO set. `inmemory` keeps it in memory; `lsm` stores it on disk using LSM trees. | +| `snapshot` | object | no | — | — | Ledger state snapshot behavior. See [config.snapshot](#configsnapshot). | +| `insert` | object | no | — | — | Upstream `insert_options`. See [config.insert](#configinsert). | +| `ipfsGateways` | array of string | no | — | — | Gateways used for offchain metadata fetching. | + +When `config` is provided, `ledgerBackend` is required within it. + +### config.runtime + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `cache` | bool | yes | `true` | Whether db-sync caches are enabled. | +| `epochTable` | bool | yes | `true` | Whether db-sync populates the epoch table. | +| `forceIndexes` | bool | yes | `false` | Whether db-sync creates indexes at startup rather than later in the sync lifecycle. | +| `metricsPort` | int32 | yes | `8080` | db-sync Prometheus metrics port. Range 1–65535. | + +### config.snapshot + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `nearTipEpoch` | int64 | yes | `580` | Epoch threshold where db-sync considers itself near tip for snapshot frequency. Minimum 0. | + +### config.insert + +Maps to upstream db-sync `insert_options`. `preset` selects a profile; the +explicit fields below are interpreted by the controller as overrides on top of +the preset. + +| Field | Type | Required | Default | Enum | Description | +| --- | --- | --- | --- | --- | --- | +| `preset` | string | yes | `full` | `full`, `only_utxo`, `only_governance`, `disable_all` | Upstream insert profile. `full` enables the normal full schema surface; `only_utxo` keeps only UTxO-oriented data; `only_governance` keeps governance-oriented data; `disable_all` keeps only the minimum block and tx data. | +| `txCbor` | bool | no | — | — | Transaction CBOR collection. | +| `txOut` | object | no | — | — | Transaction output storage. See [config.insert.txOut](#configinserttxout). | +| `ledger` | string | no | — | `enable`, `disable`, `ignore` | Ledger state maintenance and use. `enable` maintains and uses ledger-derived data; `disable` avoids maintaining ledger state; `ignore` maintains state but does not use its data. | +| `shelley` | object | no | — | — | Shelley-era table inserts. See [config.insert.shelley](#configinsertshelley). | +| `multiAsset` | object | no | — | — | Multi-asset table inserts. See [config.insert.multiAsset](#configinsertmultiasset). | +| `metadata` | object | no | — | — | Transaction metadata inserts. See [config.insert.metadata](#configinsertmetadata). | +| `plutus` | object | no | — | — | Plutus and script table inserts. See [config.insert.plutus](#configinsertplutus). | +| `governance` | bool | no | — | — | Governance-related data inserts. | +| `offchainPoolData` | bool | no | — | — | Stake pool offchain metadata fetching. | +| `offchainVoteData` | bool | no | — | — | Governance offchain metadata fetching. | +| `poolStats` | bool | no | — | — | Pool stats inserts. | +| `jsonType` | string | no | — | `text`, `jsonb`, `disable` | Upstream `json_type` option. `text` stores JSON as text; `jsonb` stores it as jsonb; `disable` disables JSON storage where supported. | +| `removeJsonbFromSchema` | bool | no | — | — | Whether db-sync removes jsonb data types from affected schema columns. | + +#### config.insert.txOut + +| Field | Type | Required | Default | Enum | Description | +| --- | --- | --- | --- | --- | --- | +| `mode` | string | no | — | `enable`, `disable`, `consumed`, `prune`, `bootstrap` | Upstream `tx_out` value. `enable` stores all inputs and outputs; `disable` disables the tx input/output tables; `consumed` stores `consumed_by_tx_id` for direct UTxO queries; `prune` periodically prunes consumed outputs; `bootstrap` delays UTxO insertion until db-sync reaches the tip. | +| `forceTxIn` | bool | no | — | — | Keeps `tx_in` populated for `consumed`, `prune`, or `bootstrap` modes. | +| `useAddressTable` | bool | no | — | — | Enables the normalized address table schema variant. | + +#### config.insert.shelley + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `enabled` | bool | no | — | Shelley-era data inserts. | +| `stakeAddresses` | array of string | no | — | Limits Shelley data to specific stake addresses. | + +#### config.insert.multiAsset + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `enabled` | bool | no | — | Multi-asset data inserts. | +| `policies` | array of string | no | — | Limits multi-asset data to specific policy hashes. | + +#### config.insert.metadata + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `enabled` | bool | no | — | Transaction metadata inserts. | +| `keys` | array of int64 | no | — | Limits metadata inserts to specific numeric labels. | + +#### config.insert.plutus + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `enabled` | bool | no | — | Plutus and script data inserts. | +| `scriptHashes` | array of string | no | — | Limits Plutus data to specific script hashes. | + +## Status + +All status fields are optional and populated by the controller. + +| Field | Type | Description | +| --- | --- | --- | +| `observedGeneration` | int64 | Most recent generation observed by the controller. | +| `endpoints` | object | Cluster-local connection details. See [status.endpoints](#statusendpoints). | +| `database` | object | Database-specific runtime details. See [status.database](#statusdatabase). | +| `sync` | object | db-sync indexing progress. See [status.sync](#statussync). | +| `placement` | object | Effective placement mode and primary-sidecar contract. See [status.placement](#statusplacement). | +| `conditions` | array | Resource conditions. See [Conditions](#conditions). | + +### status.endpoints + +| Field | Type | Description | +| --- | --- | --- | +| `postgres` | object | Postgres endpoint used by db-sync and clients. | +| `metrics` | object | db-sync Prometheus metrics endpoint. | + +Each endpoint object (`ServiceEndpointStatus`) contains: + +| Field | Type | Description | +| --- | --- | --- | +| `serviceName` | string | Kubernetes Service name. | +| `port` | int32 | Service port. | +| `url` | string | Convenience URL for protocols with a stable URL shape. | + +### status.database + +| Field | Type | Enum | Description | +| --- | --- | --- | --- | +| `acceptedIdentityFingerprint` | string | — | Database-affecting plan identity the controller accepted on owned runtime material. Mirrors the value from the db-sync state PVC annotation; not the authority for identity validation. | +| `acceptedPlacementMode` | string | `dedicatedFollower`, `primarySidecar` | Placement mode accepted for the current db-sync state. Changing it requires deleting and recreating the resource with a fresh or compatible database. | +| `authSecretName` | string | — | Same-namespace Secret with generated database credentials when the user did not provide `authSecretRef`. | + +### status.sync + +| Field | Type | Description | +| --- | --- | --- | +| `nodeBlockHeight` | int64 | Latest block height reported by the follower node. | +| `dbBlockHeight` | int64 | Latest block height inserted into Postgres. | +| `dbSlotHeight` | int64 | Latest slot inserted into Postgres. | +| `dbQueueLength` | int64 | Current db-sync database event queue length. | +| `lagBlocks` | int64 | Difference between `nodeBlockHeight` and `dbBlockHeight`. | +| `epoch` | int64 | Latest epoch observed by db-sync. | + +### status.placement + +| Field | Type | Enum | Description | +| --- | --- | --- | --- | +| `mode` | string | `dedicatedFollower`, `primarySidecar` | Effective placement mode for this reconcile. | +| `primarySidecar` | object | — | Attachable material contract published when `SidecarMaterialReady=True`. | + +`primarySidecar` contains: + +| Field | Type | Description | +| --- | --- | --- | +| `networkName` | string | Referenced `CardanoNetwork` name this sidecar material is valid for. | +| `revision` | string | Opaque sha256 rollout revision over all sidecar-mounted material. | +| `resources` | object | `CardanoDBSync`-owned resources mounted by the primary Pod sidecar. | + +`primarySidecar.resources` contains: + +| Field | Type | Description | +| --- | --- | --- | +| `configMapName` | string | db-sync configuration ConfigMap name. | +| `pgpassSecretName` | string | db-sync pgpass Secret name. | +| `statePVCName` | string | db-sync state PVC name. | +| `metricsServiceName` | string | db-sync metrics Service name. | + +### Conditions + +`status.conditions` is a list of standard `metav1.Condition` objects keyed by +`type`. Each entry has `type`, `status` (`True`, `False`, or `Unknown`), +`reason`, `message`, `lastTransitionTime`, and `observedGeneration`. + +| Type | Meaning | +| --- | --- | +| `Ready` | db-sync is usable through its published database endpoint. | +| `FollowerNodeReady` | The colocated follower node is running. | +| `NodeSocketReady` | The node socket used by db-sync is reachable. | +| `SidecarMaterialReady` | Primary-sidecar mounted material is attachable. | +| `PostgresReady` | Postgres is running and accepting local connections. | +| `DBSyncReady` | The db-sync process is running. | +| `Synced` | db-sync has caught up to the node tip. | +| `Progressing` | The resource is being created or updated. | +| `Degraded` | The resource failed to reach or maintain desired state. | diff --git a/docs/reference/cardanonetwork.md b/docs/reference/cardanonetwork.md new file mode 100644 index 00000000..ccee90a1 --- /dev/null +++ b/docs/reference/cardanonetwork.md @@ -0,0 +1,340 @@ +# CardanoNetwork + +`CardanoNetwork` is the YACD custom resource that declares a single Cardano +development network: one primary [cardano-node](https://developers.cardano.org) +workload plus its chain APIs ([Ogmios](https://ogmios.dev), +[Kupo](https://cardanosolutions.github.io/kupo/), a local faucet, and an +optional developer wallet). + +| | | +| --- | --- | +| API group/version | `yacd.meigma.io/v1alpha1` | +| Kind | `CardanoNetwork` | +| List kind | `CardanoNetworkList` | +| Plural | `cardanonetworks` | +| Singular | `cardanonetwork` | +| Scope | Namespaced | +| Status subresource | Enabled | + +A network is created in one of two **modes**: `local` (YACD generates and owns a +fresh devnet) or `public` (the node joins a known public profile). Exactly one of +`spec.local` or `spec.public` must be present, matching `spec.mode`. + +Copy-paste manifests live in [recipes](../recipes.md). The companion CLI and its +flags are documented in the [CLI reference](cli.md). For the sibling resource +that attaches cardano-db-sync, see +[CardanoDBSync](cardanodbsync.md). + +## TypeMeta and metadata + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `apiVersion` | string | yes | `yacd.meigma.io/v1alpha1` | +| `kind` | string | yes | `CardanoNetwork` | +| `metadata` | [ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta) | no | Standard object metadata | +| `spec` | [CardanoNetworkSpec](#spec) | yes | Desired state | +| `status` | [CardanoNetworkStatus](#status) | no | Observed state | + +## spec + +`spec` defines the desired state of the network. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `mode` | string enum | yes | — | Network mode. One of `local`, `public`. | +| `node` | [CardanoNodeSpec](#specnode) | yes | — | Primary cardano-node workload, shared by both modes. | +| `local` | [LocalNetworkSpec](#speclocal) | conditional | — | Generated local devnet. Required when `mode: local`, forbidden otherwise. | +| `public` | [PublicNetworkSpec](#specpublic) | conditional | — | Public-profile join settings. Required when `mode: public`, forbidden otherwise. | +| `chainAPI` | [ChainAPISpec](#specchainapi) | no | — | Network-facing APIs exposed next to the primary node. | + +### Validation rules + +The spec carries CEL validation enforced by the API server: + +- **Mode must match exactly one of `spec.local` or `spec.public`.** When + `mode` is `local`, `local` must be set and `public` must be absent. When + `mode` is `public`, `public` must be set and `local` must be absent. +- **`bootstrap.mithril` is required only when `public.profile` is `mainnet`.** + When the profile is `mainnet`, `public.bootstrap.mithril` must be present. + For any other profile, `public.bootstrap` must be absent. + +### spec.node + +`node` configures the primary cardano-node workload. + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `version` | string | yes | `11.0.1` | — | cardano-node release used by the managed node image. | +| `image` | string | no | — | — | Full cardano-node image reference. When omitted, the controller derives an image from `version`. | +| `port` | integer (int32) | yes | `3001` | `1`–`65535` | Node-to-node TCP port exposed by the node Service. | +| `storage` | [NodeStorageSpec](#specnodestorage) | no | — | — | Persistent node database storage. | +| `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Primary cardano-node container resources. | + +#### spec.node.storage + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `size` | quantity | yes | `10Gi` | Requested persistent volume size for the node database. | +| `storageClassName` | string | no | — | StorageClass used for the node database PVC. | + +### spec.local + +`local` configures a YACD-generated local devnet. Required when `mode: local`. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `networkMagic` | integer (int64) | yes | `42` | Testnet magic used by local node and client commands. Minimum `0`. | +| `era` | string enum | yes | `conway` | Newest ledger era for the generated network. One of `babbage`, `conway`. | +| `timing` | [LocalNetworkTimingSpec](#speclocaltiming) | yes | — | Slot and epoch duration for the generated network. | +| `topology` | [LocalNetworkTopologySpec](#speclocaltopology) | yes | — | Initial local network topology. | +| `genesis` | [LocalGenesisSpec](#speclocalgenesis) | no | — | Local genesis material generated by YACD. | + +#### spec.local.timing + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `slotLength` | duration | yes | `100ms` | — | Local network slot duration. | +| `epochLength` | integer (int64) | yes | `500` | minimum `1` | Number of slots in an epoch. | + +#### spec.local.topology + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `pools` | [LocalPoolTopologySpec](#speclocaltopologypools) | yes | — | Generated stake pool nodes. | + +##### spec.local.topology.pools + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `count` | integer (int32) | yes | `1` | minimum `1` | Number of generated pool nodes. The initial controller may reject values above one until multi-node reconciliation is implemented. | +| `defaults` | [LocalPoolDefaultsSpec](#speclocaltopologypoolsdefaults) | no | — | Shared pool economics for generated pools. | + +###### spec.local.topology.pools.defaults + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `costLovelace` | integer (int64) | no | — | minimum `0` | Fixed pool cost used for generated pools. | +| `pledgeLovelace` | integer (int64) | no | — | minimum `0` | Pool pledge used for generated pools. | +| `margin` | string | no | — | pattern `^(0(\.[0-9]+)?\|1(\.0+)?)$` | Pool margin as a decimal string in the inclusive range `[0, 1]`. A string to avoid floating-point behavior in the API. | + +#### spec.local.genesis + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `profile` | string enum | yes | `default` | one of `default`, `zero-fee`, `zero-min-utxo`, `zero-fee-and-min-utxo` | Curated genesis preset. | +| `securityParameter` | integer (int32) | no | — | minimum `1` | Generated Shelley security parameter. | +| `maxLovelaceSupply` | integer (int64) | no | — | minimum `1` | Generated Shelley max lovelace supply. | +| `delegatedSupply` | integer (int64) | no | — | minimum `0` | Lovelace supply initially delegated to generated pools. | +| `protocolVersion` | [ProtocolVersionSpec](#speclocalgenesisprotocolversion) | no | — | — | Initial protocol version in generated genesis material. | + +##### spec.local.genesis.protocolVersion + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `major` | integer (int32) | yes | — | minimum `0` | Protocol major version. | +| `minor` | integer (int32) | yes | — | minimum `0` | Protocol minor version. | + +The genesis `profile` values control fee and minimum-UTxO behavior: + +| Profile | Effect | +| --- | --- | +| `default` | YACD's normal local development defaults. | +| `zero-fee` | Removes transaction fees for fast local tests. | +| `zero-min-utxo` | Removes the minimum UTxO value. | +| `zero-fee-and-min-utxo` | Removes both fees and the minimum UTxO value. | + +### spec.public + +`public` configures a node that joins a public network profile. Required when +`mode: public`. + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `profile` | string enum | yes | — | Public network profile. One of `preprod`, `preview`, `mainnet`. | +| `bootstrap` | [PublicNetworkBootstrapSpec](#specpublicbootstrap) | conditional | — | Explicit bootstrap behavior. Required (and only allowed) when `profile: mainnet`. | + +!!! warning "Mainnet is opt-in and expensive" + `mainnet` cannot sync from genesis in a development setting. It requires a + [Mithril](https://mithril.network) bootstrap (`public.bootstrap.mithril`) + to seed the node database, large persistent storage, and explicit operator + intent. The CEL rule above forbids `bootstrap` on any non-mainnet profile. + +#### spec.public.bootstrap + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `mithril` | [MithrilBootstrapSpec](#specpublicbootstrapmithril) | no | — | Mithril Cardano database bootstrap. Required when `profile: mainnet`. | + +##### spec.public.bootstrap.mithril + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `image` | string | no | `ghcr.io/input-output-hk/mithril-client:main-2478748` | minLength `1` | Mithril client image used by the bootstrap init container. | +| `snapshot` | string | no | `latest` | minLength `1` | Mithril Cardano database snapshot digest to download. `latest` selects the latest available snapshot. | + +### spec.chainAPI + +`chainAPI` configures APIs exposed next to the primary node. Ogmios and Kupo are +enabled by default. The faucet and wallet are opt-in because they expose a +spending endpoint. + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `ogmios` | [OgmiosSpec](#specchainapiogmios) | no | Ogmios sidecar and Service. | +| `kupo` | [KupoSpec](#specchainapikupo) | no | Kupo sidecar and Service. | +| `faucet` | [FaucetSpec](#specchainapifaucet) | no | Faucet sidecar and Service. | +| `wallet` | [WalletSpec](#specchainapiwallet) | no | Pre-funded developer wallet for local networks. | + +#### spec.chainAPI.ogmios + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `enabled` | boolean | yes | `true` | — | Whether the Ogmios sidecar is deployed. | +| `image` | string | yes | `cardanosolutions/ogmios:v6.14.0` | — | Ogmios image reference. | +| `port` | integer (int32) | yes | `1337` | `1`–`65535` | Ogmios service port. | +| `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Ogmios container resources. | + +#### spec.chainAPI.kupo + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `enabled` | boolean | yes | `true` | — | Whether the Kupo sidecar is deployed. | +| `image` | string | yes | `cardanosolutions/kupo:v2.11.0` | — | Kupo image reference. | +| `port` | integer (int32) | yes | `1442` | `1`–`65535` | Kupo service port. | +| `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Kupo container resources. | + +#### spec.chainAPI.faucet + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `enabled` | boolean | yes | `false` | — | Whether the faucet sidecar is deployed. | +| `image` | string | no | — | — | Overrides the faucet image reference. When omitted, the controller uses its configured default. Overrides must use the same repository as the controller's default faucet image; tag or digest may vary. | +| `port` | integer (int32) | yes | `8080` | `1`–`65535` | Faucet service port. | +| `defaultSource` | string | yes | `utxo1` | — | Generated cardano-testnet UTxO source used when a request does not select one explicitly. | +| `minTopUpLovelace` | integer (int64) | yes | `1000000` | minimum `1` | Minimum exact top-up amount. | +| `maxTopUpLovelace` | integer (int64) | yes | `10000000000` | minimum `1` | Maximum exact top-up amount. | +| `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Faucet container resources. | + +!!! warning "The faucet is local-only and spends funds" + The faucet exposes a spending endpoint and is intended for local + development networks only. Mutating requests require the bearer token + published in `status.faucet.authSecretName`. + +#### spec.chainAPI.wallet + +`wallet` configures a pre-funded developer wallet that the controller +bootstraps for local development networks. It requires the faucet and Kupo to be +enabled and is supported in local mode only. + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `enabled` | boolean | yes | `false` | — | Whether the controller bootstraps a developer wallet. | +| `fundingLovelace` | integer (int64) | yes | `100000000000` | minimum `1` | Amount the controller funds the wallet with on first bring-up, through the faucet. Must not exceed the faucet's `maxTopUpLovelace`. | + +## status + +`status` reports the observed state of the network. All fields are populated by +the controller and are read-only. + +| Field | Type | Description | +| --- | --- | --- | +| `observedGeneration` | integer (int64) | Most recent generation observed by the controller. | +| `network` | [CardanoNetworkIdentityStatus](#statusnetwork) | Resolved network identity once chain material is generated or loaded. | +| `endpoints` | [CardanoNetworkEndpointsStatus](#statusendpoints) | Cluster-local connection details for clients and supporting controllers. | +| `faucet` | [FaucetStatus](#statusfaucet) | Faucet-specific runtime details. | +| `wallet` | [WalletStatus](#statuswallet) | Bootstrapped developer wallet details. | +| `sync` | [CardanoNetworkSyncStatus](#statussync) | Primary node chain synchronization status inferred from in-cluster sources. | +| `conditions` | [][Condition](#conditions) | Current state of the resource (list-map keyed by `type`). | + +### status.network + +| Field | Type | Description | +| --- | --- | --- | +| `mode` | string enum | Resolved network mode. One of `local`, `public`. | +| `localnetFingerprint` | string | Accepted fingerprint for generated localnet inputs. Changing those inputs requires deleting and recreating the CardanoNetwork. | +| `networkFingerprint` | string | Accepted fingerprint for the resolved network profile, independent of local/public. Changing it requires deleting and recreating the CardanoNetwork. | +| `networkMagic` | integer (int64) | Resolved Cardano network magic. | +| `profile` | string enum | Resolved public network profile, when `mode` is `public`. One of `preprod`, `preview`, `mainnet`. | +| `era` | string enum | Newest resolved ledger era known to the controller. One of `babbage`, `conway`. | + +### status.endpoints + +Each endpoint is a [ServiceEndpointStatus](#serviceendpointstatus). + +| Field | Type | Description | +| --- | --- | --- | +| `nodeToNode` | ServiceEndpointStatus | Primary node-to-node endpoint. | +| `ogmios` | ServiceEndpointStatus | Ogmios JSON/RPC endpoint. | +| `kupo` | ServiceEndpointStatus | Kupo chain index HTTP endpoint. | +| `faucet` | ServiceEndpointStatus | Local development faucet HTTP endpoint. | +| `artifacts` | ServiceEndpointStatus | cardano-tools `serve` HTTP endpoint exposing the staged network artifact files and `manifest.json`. | + +#### ServiceEndpointStatus + +| Field | Type | Description | +| --- | --- | --- | +| `serviceName` | string | Kubernetes Service name. | +| `port` | integer (int32) | Service port. | +| `url` | string | Convenience URL for protocols with a stable URL shape. | + +### status.faucet + +| Field | Type | Description | +| --- | --- | --- | +| `authSecretName` | string | Same-namespace Secret containing the bearer token used by mutating faucet requests. | + +### status.wallet + +| Field | Type | Description | +| --- | --- | --- | +| `address` | string | Wallet's enterprise testnet payment address (`addr_test...`). | +| `keySecretName` | string | Same-namespace Secret holding the wallet's signing and verification key envelopes. | +| `funded` | boolean | Whether the wallet's funding has been confirmed on-chain. | +| `fundedTxID` | string | Faucet transaction that funded the wallet. | + +### status.sync + +`sync` reports the primary node's observed synchronization state using only +in-cluster sources. + +| Field | Type | Constraints | Description | +| --- | --- | --- | --- | +| `source` | string enum | one of `ogmios` | Probe source that produced this sync status. | +| `connectionStatus` | string | — | Ogmios health connection status. | +| `tip` | [CardanoNetworkTipStatus](#statussynctip) | — | Last known chain tip reported by Ogmios. | +| `lastTipUpdate` | timestamp (date-time) | — | Timestamp Ogmios reported for the last known tip update. | +| `observedAt` | timestamp (date-time) | — | Controller time when this sync status was probed. | +| `networkSynchronization` | number (float64) | `0`–`1` | Ogmios' synchronization estimate, rounded to five decimals when reported. | +| `inferredTipSlot` | integer (int64) | minimum `0` | Wall-clock network slot inferred from the published Shelley genesis timing. | +| `lagSlots` | integer (int64) | minimum `0` | `max(inferredTipSlot - tip.slot, 0)`. | +| `lagSeconds` | integer (int64) | minimum `0` | `lagSlots` converted through `slotLength` and rounded up to a whole number of seconds. | + +#### status.sync.tip + +| Field | Type | Constraints | Description | +| --- | --- | --- | --- | +| `slot` | integer (int64) | minimum `0` | Tip slot reported by Ogmios. | +| `blockHeight` | integer (int64) | minimum `0` | Tip block height reported by Ogmios. | +| `hash` | string | — | Tip hash reported by Ogmios. | + +### conditions + +`conditions` is a list of standard +[metav1.Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#condition-v1-meta) +entries, keyed by a unique `type`. Each condition `status` is one of `True`, +`False`, or `Unknown`. + +| Type | Meaning | +| --- | --- | +| `Ready` | The network is usable through its published endpoints. | +| `DBSyncAttachmentReady` | The primary-sidecar db-sync attachment is not blocking the primary Pod. | +| `NodeReady` | The primary node container is running. | +| `NodeSynchronized` | The primary node is caught up to its inferred network tip. | +| `NodeProgressing` | The primary node tip is advancing or already synchronized. | +| `ArtifactsReady` | The network artifact bundle is staged and served over HTTP. | +| `OgmiosReady` | Ogmios is enabled and connected to the primary node. | +| `KupoReady` | Kupo is enabled and synchronized enough to serve its API. | +| `FaucetReady` | The faucet is enabled and available through its Service. | +| `WalletReady` | The developer wallet is bootstrapped and funded on-chain. | +| `Progressing` | The resource is being created or updated. | +| `Degraded` | The resource failed to reach or maintain its desired state. | diff --git a/docs/reference/cli.md b/docs/reference/cli.md new file mode 100644 index 00000000..522b0350 --- /dev/null +++ b/docs/reference/cli.md @@ -0,0 +1,447 @@ +# CLI reference + +The `yacd` developer CLI manages YACD environments in a Kubernetes cluster and +wires local tools and tests to a running network. This page is the single source +of truth for every command, flag, default, and the `YACD_*` environment +contract. Other pages link here rather than restating flags. + +## Synopsis + +```text +yacd [command] [flags] +``` + +Each command that targets a network takes a positional `NAME`. `NAME` becomes +the CardanoNetwork name, and the namespace defaults to `NAME` unless you pass +`--namespace`. Both `NAME` and the resolved namespace must be valid DNS-1123 +labels (lowercase alphanumeric and `-`); invalid input is rejected. + +Commands: + +| Command | Summary | +| --- | --- | +| `devnet` | Bring up a local Cardano devnet (cluster, operator, and a funded network). | +| `devnet down` | Delete the managed devnet cluster. | +| `devnet status` | Show the managed devnet cluster, operator, and network status. | +| `up NAME` | Create or update a YACD environment and wait for readiness. | +| `down NAME` | Delete a YACD environment and wait for clean removal. | +| `list` | List YACD environments in the cluster. | +| `info NAME` | Print CardanoNetwork status and connection information. | +| `topup NAME` | Submit a faucet top-up. | +| `run NAME [-- command ...]` | Run a command (or a shell) on the host with the `YACD_*` environment wired to forwarded endpoints. | +| `connect NAME` | Forward a network's endpoints and hold them open until interrupted. | +| `exec NAME -- command ...` | Run a command inside the primary node Pod (for socket-bound tools). | +| `completion` | Generate a shell autocompletion script. | + +## Global flags + +These persistent flags apply to every command. Each binds to a `YACD_*` +environment variable through the `YACD` env prefix; precedence is **flag > env > +default**. + +| Flag | Short | Type | Default | Env override | Meaning | +| --- | --- | --- | --- | --- | --- | +| `--kubeconfig` | | string | `""` | `YACD_KUBECONFIG` | Path to the kubeconfig file. Empty defers to standard loading rules. | +| `--context` | | string | `""` | `YACD_CONTEXT` | Kubeconfig context to use. Empty defers to the current context. | +| `--namespace` | `-n` | string | `""` | `YACD_NAMESPACE` | Kubernetes namespace. Empty defers to `NAME` (per-environment namespace) or kubeconfig defaults. | +| `--log-level` | | string | `info` | `YACD_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error`. | +| `--log-format` | | string | `text` | `YACD_LOG_FORMAT` | Log format: `text`, `json`. | +| `--help` | `-h` | bool | `false` | | Help for the command. | +| `--version` | `-v` | bool | `false` | | Print the version. Root command only. | + +!!! note "Env override naming" + The env prefix is `YACD` and flag names are upper-cased with `-` replaced by + `_`. `YACD_NAMESPACE` is both the override for `--namespace` and a value the + CLI publishes into the `YACD_*` contract; see [the contract table](#the-yacd-environment-contract). + +`yacd --version` prints `yacd () built `. + +## devnet + +```text +yacd devnet [flags] +``` + +Brings a managed [k3d](https://k3d.io) cluster, the operator, and a default +funded local network to a ready state in one command. Takes no `NAME` and no +`--namespace` (it manages a fixed `devnet` network in a `devnet` namespace). + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--bare` | bool | `false` | Stop after installing the operator; apply no network. | +| `--timeout` | duration | `12m0s` | Maximum time to wait for the cluster, operator, and network. Must be greater than 0. | + +On success it prints the cluster context, the operator version, and (unless +`--bare`) the Ogmios and Kupo endpoints, the funded wallet address, and a +copy-pasteable `yacd exec` tip-query hint. + +### devnet down + +```text +yacd devnet down [flags] +``` + +Deletes the managed devnet cluster and restores the prior kubectl context. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--timeout` | duration | `5m0s` | Maximum time to wait for the cluster to be deleted. Must be greater than 0. | + +### devnet status + +```text +yacd devnet status +``` + +Read-only unified view of the managed cluster, operator, and networks. Takes no +flags beyond the [global flags](#global-flags). Prints a one-line hint when no +managed cluster exists. + +## up + +```text +yacd up NAME [flags] +``` + +Loads a developer environment file, renders it into a CardanoNetwork under the +resolved identity, creates the namespace if needed, and server-side-applies the +network. Unless `--wait=false`, it then polls until the network is Ready or the +timeout elapses. + +| Flag | Short | Type | Default | Meaning | +| --- | --- | --- | --- | --- | +| `--file` | `-f` | string | `""` | Developer environment file. Required. | +| `--dry-run` | | bool | `false` | Render the manifest to stdout without applying it. | +| `--allow-mainnet` | | bool | `false` | Allow applying a mainnet CardanoNetwork. | +| `--wait` | | bool | `true` | Wait for the CardanoNetwork to become ready. | +| `--timeout` | | duration | `12m0s` | Maximum time to wait for readiness. Must be greater than 0 when `--wait` is set. | + +!!! warning "Mainnet requires `--allow-mainnet`" + Applying a mainnet network without `--allow-mainnet` is rejected because + mainnet deployments create large persistent volumes and bootstrap from + [Mithril](https://mithril.network). `--dry-run` renders a mainnet manifest + without the flag but applies nothing. + +## down + +```text +yacd down NAME [flags] +``` + +Deletes the named CardanoNetwork and, unless `--wait=false`, blocks until the +object and its garbage-collected children are gone. Deletion is idempotent: a +network that is already absent is reported as success. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--wait` | bool | `true` | Wait for the CardanoNetwork and its resources to be removed. | +| `--timeout` | duration | `5m0s` | Maximum time to wait for removal. Must be greater than 0 when `--wait` is set. | + +## list + +```text +yacd list [flags] +``` + +Lists CardanoNetworks in the active namespace (or across all namespaces with +`-A`), projecting each into `name`, `namespace`, `mode`, `ready`, and published +`endpoints`. Renders an aligned table by default or JSON with `--json`. + +| Flag | Short | Type | Default | Meaning | +| --- | --- | --- | --- | --- | +| `--all-namespaces` | `-A` | bool | `false` | List CardanoNetworks across all namespaces. | +| `--json` | | bool | `false` | Print machine-readable JSON. | + +The table columns are `NAME`, `NAMESPACE`, `MODE`, `READY`, `ENDPOINTS`. +`READY` reflects a fresh `Ready` condition observed as `True` (a stale status is +reported as not ready). `ENDPOINTS` is a comma-separated list of published +endpoint names (`node-to-node`, `ogmios`, `kupo`, `faucet`) or `-` when none are +published yet. + +The `--json` output is an array of objects with fields: + +| Field | Type | Meaning | +| --- | --- | --- | +| `name` | string | CardanoNetwork name. | +| `namespace` | string | CardanoNetwork namespace. | +| `mode` | string | Requested network mode (`local` or `public`). | +| `ready` | bool | Fresh `Ready` condition observed as `True`. | +| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo`, `faucet` URLs; empty when unpublished. | + +## info + +```text +yacd info NAME [flags] +``` + +Fetches the named CardanoNetwork and prints its status and connection +information, as human-readable text by default or JSON with `--json`. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--json` | bool | `false` | Print machine-readable JSON. | + +The `--json` object has stable field names. The `conditions` array is always +present (possibly empty); nil sub-statuses are omitted rather than emitted as +empty objects. + +| Field | Type | Meaning | +| --- | --- | --- | +| `name` | string | CardanoNetwork name. | +| `namespace` | string | CardanoNetwork namespace. | +| `observedGeneration` | int | Last generation the controller observed. Omitted when 0. | +| `network` | object | `mode`, `localnetFingerprint`, `networkMagic`, `profile`, `era`. | +| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo`, `faucet`, each `{serviceName, port, url}` or absent. | +| `faucet` | object | `{authSecretName}`. Omitted when no faucet is published. | +| `wallet` | object | `{address, keySecretName, funded}`. Omitted when no developer wallet exists. | +| `conditions` | array | Each `{type, status, reason, message, observedGeneration, lastTransitionTime}` (RFC3339 timestamp). | + +## topup + +```text +yacd topup NAME [flags] +``` + +Submits a faucet top-up. The flow resolves the target faucet URL (preferring the +cluster-published endpoint unless `--faucet-url` overrides it), gates token +transmission through the trust checks below, fetches the auth token from the +published Secret, then `POST`s to the faucet's `/v1/topups` endpoint. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--address` | string | `""` | Destination Cardano testnet address. Required. | +| `--lovelace` | int | `0` | Exact lovelace amount to send. Required; must be greater than 0. | +| `--source` | string | `""` | Faucet source name, for example `utxo1`. Empty lets the faucet pick a default. | +| `--faucet-url` | string | `""` | Override the faucet URL from CardanoNetwork status. | +| `--trust-faucet-url` | bool | `false` | Allow sending the faucet auth token to a custom non-loopback URL. | +| `--allow-insecure-faucet-url` | bool | `false` | Allow trusted custom non-loopback HTTP faucet URLs. | +| `--json` | bool | `false` | Print machine-readable JSON. | +| `--await` | bool | `false` | Wait for the funding transaction to be confirmed on-chain (requires Kupo). | +| `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. Must be greater than 0. | +| `--kupo-url` | string | `""` | Kupo URL for `--await`. Falls back to `YACD_KUPO_URL`. | + +The default target requires the CardanoNetwork to be faucet-ready: a fresh +status with `Ready` and `FaucetReady` conditions `True`, a published faucet +endpoint, and a published faucet auth Secret. + +The cluster-published faucet URL is a cluster-internal Service address +(`http://-faucet..svc.cluster.local:`). It resolves +from an in-cluster caller, but not from your host: run `topup` under +[`yacd run`](#run), which forwards the faucet and exposes it as +`$YACD_FAUCET_URL`, then pass `--faucet-url "$YACD_FAUCET_URL"`. + +!!! warning "The faucet token leaves the cluster only with explicit acks" + By default, the token is sent only to the cluster-published faucet URL or a + loopback target. `--faucet-url` pointing at a different non-loopback host + requires `--trust-faucet-url`; a trusted `http://` (plaintext) host + additionally requires `--allow-insecure-faucet-url`. These gates exist to + prevent token exfiltration and plaintext eavesdropping. + +The `--json` output mirrors the faucet's success envelope: + +| Field | Type | Meaning | +| --- | --- | --- | +| `txId` | string | Funding transaction id. | +| `source` | string | Faucet source the funds came from. | +| `sourceAddress` | string | Source address. | +| `destinationAddress` | string | Address that was funded. | +| `lovelace` | int | Lovelace sent. | + +## run + +```text +yacd run NAME [-- command [args...]] [flags] +``` + +Establishes scoped port-forwards to the network's chain-API endpoints, injects +the [`YACD_*` environment](#the-yacd-environment-contract), and execs the command +(or your `$SHELL`, falling back to `/bin/sh`, when none is given) on the host +with that environment. The forwards are torn down when the command exits. + +Put `--` before any command that takes its own flags so they are passed through +to the command instead of being parsed by `yacd`. + +```sh +# Run a test suite against the network (note the -- before the command) +yacd run my-net -- go test ./e2e/... + +# Open a shell with the YACD_* environment set +yacd run my-net +``` + +`run` has no command-specific flags beyond the [global flags](#global-flags). +The child inherits the CLI's stdio and process group, so an interactive Ctrl-C +reaches it. The child's exit status is propagated to your shell; a process +killed by a signal reports `128+signal` (for example `130` for SIGINT). If the +forwards drop while the command runs, `run` exits non-zero and reports the lost +connection. + +## connect + +```text +yacd connect NAME [flags] +``` + +Establishes supervised port-forwards to a network's chain-API endpoints, writes +the loopback URLs to `.yacd//endpoints.json` (or +`.yacd///endpoints.json` when `--namespace` is set), prints +them, and holds them open until interrupted (Ctrl-C). Run it in one terminal and +your tools in another. Dropped forwards are re-established automatically (with a +freshly resolved Pod and new local ports). On exit (or before each +re-establish), the endpoints file is removed. + +`connect` has no command-specific flags beyond the [global flags](#global-flags). + +!!! note "Loopback ports are ephemeral and token-free" + The endpoints file never contains the faucet token, and its ports are only + live while `connect` is running. See the [endpoints.json schema](#the-endpointsjson-schema). + +## exec + +```text +yacd exec NAME -- command [args...] [flags] +``` + +Runs a command inside the primary `cardano-node` Pod with kubectl-exec +semantics, for tools that reach the node over its local Unix socket (notably +`cardano-cli`) rather than over a forwarded TCP port. `CARDANO_NODE_SOCKET_PATH` +and the [`YACD_*` variables](#the-yacd-environment-contract) are set in the pod +environment, so `cardano-cli` finds the socket automatically. A command is +required. + +```sh +# cardano-cli reads CARDANO_NODE_SOCKET_PATH from the pod environment: +yacd exec my-net -- cardano-cli query tip --testnet-magic 42 + +# To interpolate YACD_* variables into arguments, run a shell explicitly: +yacd exec my-net -- sh -c 'cardano-cli query tip --testnet-magic "$YACD_NETWORK_MAGIC"' + +# From a terminal, open an interactive shell in the node Pod: +yacd exec my-net -- sh +``` + +`exec` has no command-specific flags beyond the [global flags](#global-flags). +The command is run directly, not through a shell, so `$VAR` references in +arguments are **not** expanded; wrap the command in `sh -c '...'` to interpolate +`YACD_*` variables. When both stdin and stdout are terminals, `exec` attaches an +interactive TTY; piped or non-terminal (CI) invocations stream without one. The +command's exit code is propagated to the caller. `exec` requires the +CardanoNetwork to be Ready. + +## The YACD environment contract + +`run`, `connect`, and `exec` publish a stable, versioned set of `YACD_*` +variables (contract version 1). Tests and tooling read these instead of parsing +any YACD file. The variable names are identical whether a command runs on the +host (`run`, over port-forwards) or inside the primary Pod (`exec`, over cluster +DNS); only the values adapt. Adding a variable is backward compatible; renaming +or removing one is a breaking change to the contract. + +| Variable | Host (`run`) value | In-pod (`exec`) value | Present when | +| --- | --- | --- | --- | +| `YACD_NETWORK` | network name | network name | always | +| `YACD_NAMESPACE` | network namespace | network namespace | always | +| `YACD_NETWORK_MAGIC` | network magic (integer) | network magic (integer) | when the controller has published the network magic | +| `YACD_OGMIOS_URL` | loopback URL (scheme preserved, e.g. `ws://`) | published ClusterIP URL | when Ogmios is published and forwarded | +| `YACD_KUPO_URL` | loopback URL | published ClusterIP URL | when Kupo is published and forwarded | +| `YACD_FAUCET_URL` | loopback URL | published ClusterIP URL | when the faucet is published and forwarded | +| `YACD_FAUCET_TOKEN` | faucet auth Bearer token | *not set* | host `run` only, when the token is non-empty | +| `CARDANO_NODE_SOCKET_PATH` | *not set* | `/ipc/node.socket` | in-pod `exec` only | + +Notes: + +- On the host, each forwarded chain endpoint URL is rewritten onto + `127.0.0.1:`. Only host and port change; the scheme, path, query, + and fragment carry through unchanged (a `ws://` Ogmios endpoint stays `ws://`). +- In the Pod, the published ClusterIP URLs are passed through verbatim. +- The node-to-node endpoint is intentionally excluded: it is a TCP peer + protocol, not something host or in-pod test tooling speaks. +- `CARDANO_NODE_SOCKET_PATH` is unprefixed because that is the name + `cardano-cli` already expects. + +!!! warning "`YACD_FAUCET_TOKEN` is host-only" + `exec` deliberately omits `YACD_FAUCET_TOKEN`: a Bearer token in the exec + argv would land in apiserver audit logs and `/proc`. In-pod tooling does not + need it. `yacd topup` reads the token from the cluster directly. + +`yacd topup --await` reads `--kupo-url` from `YACD_KUPO_URL` through this +contract, so it works unchanged when run under `yacd run`. + +## The endpoints.json schema + +`yacd connect` writes a token-free connection document to +`.yacd//endpoints.json` (mode `0600`, in a `0700` directory), or +`.yacd///endpoints.json` when `--namespace` differs from +`NAME`. The field names are stable across releases. + +```json +{ + "network": "my-net", + "namespace": "my-net", + "networkMagic": 42, + "ogmiosUrl": "ws://127.0.0.1:51820", + "kupoUrl": "http://127.0.0.1:51821", + "faucetUrl": "http://127.0.0.1:51822" +} +``` + +| Field | Type | Meaning | +| --- | --- | --- | +| `network` | string | CardanoNetwork name. Always present. | +| `namespace` | string | CardanoNetwork namespace. Always present. | +| `networkMagic` | int | Network magic. Omitted until the controller publishes it. | +| `ogmiosUrl` | string | Loopback Ogmios URL. Omitted when not forwarded. | +| `kupoUrl` | string | Loopback Kupo URL. Omitted when not forwarded. | +| `faucetUrl` | string | Loopback faucet URL. Omitted when not forwarded. | + +The document deliberately never carries the faucet token, and the ports it lists +are only live while `connect` is running. The file is removed on disconnect. + +## Install + +Download the `yacd` binary for your platform from the +[releases page](https://github.com/meigma/yacd/releases) and place it on your +`PATH`, then verify: + +```sh +yacd --version +``` + +## Shell completion + +`yacd completion ` generates an autocompletion script. Supported shells +are `bash`, `zsh`, `fish`, and `powershell`. Each script accepts +`--no-descriptions` to disable completion descriptions. + +=== "macOS" + + ```sh + # zsh + yacd completion zsh > $(brew --prefix)/share/zsh/site-functions/_yacd + + # bash (requires the bash-completion package) + yacd completion bash > $(brew --prefix)/etc/bash_completion.d/yacd + ``` + +=== "Linux" + + ```sh + # zsh + yacd completion zsh > "${fpath[1]}/_yacd" + + # bash (requires the bash-completion package) + yacd completion bash > /etc/bash_completion.d/yacd + ``` + +Start a new shell for the setup to take effect. To load completions for the +current session only: + +```sh +source <(yacd completion zsh) # or bash, fish +``` + +For `zsh`, if completion is not yet enabled, run once: + +```sh +echo "autoload -U compinit; compinit" >> ~/.zshrc +``` diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md new file mode 100644 index 00000000..58158a09 --- /dev/null +++ b/docs/reference/configuration.md @@ -0,0 +1,179 @@ +# Configuration + +Reference for configuring the YACD operator. There are two layers: + +1. **Helm chart values** — what you set in `values.yaml` (or `--set`) when you + install the chart. These render the operator Deployment, RBAC, metrics + Service, and the optional Kyverno image-verification policy. +2. **Manager flags** — the command-line flags the manager process accepts. The + chart translates the relevant values into these flags automatically; set them + directly only when running the manager binary outside the chart. + +This page documents the operator's own configuration. For the `yacd` CLI flags, +environment variables, and `endpoints.json` schema, see +[CLI reference](cli.md). For the per-network and per-db-sync resource fields, see +[CardanoNetwork reference](cardanonetwork.md) and +[CardanoDBSync reference](cardanodbsync.md). + +## Helm chart values + +Defaults below are from `charts/yacd/values.yaml`. Only user-relevant keys are +listed. + +### Images + +| Key | Default | Meaning | +| --- | --- | --- | +| `image.repository` | `ghcr.io/meigma/yacd` | Manager image repository. | +| `image.tag` | `""` | Manager image tag. Empty uses the chart `appVersion`. | +| `image.digest` | `""` | Manager image digest (`sha256:...`). When set, it takes precedence over `image.tag`. | +| `image.pullPolicy` | `IfNotPresent` | Manager image pull policy. | +| `faucet.image.repository` | `ghcr.io/meigma/yacd/faucet` | Faucet image used for CardanoNetwork faucet sidecars. | +| `faucet.image.tag` | `""` | Faucet image tag. Empty uses the chart `appVersion`. | +| `faucet.image.digest` | `""` | Faucet image digest. When set, it takes precedence over the tag. | +| `cardanoTestnet.image.repository` | `""` | Overrides the cardano-testnet tools image (create-env init container, faucet source-address init container, and the primary cardano-node container when `spec.node.image` is unset). Empty uses the operator's built-in versioned reference. | +| `cardanoTestnet.image.tag` | `""` | Tag for the cardano-testnet override. Applied only when `repository` is set. | +| `cardanoTestnet.image.digest` | `""` | Digest for the cardano-testnet override. When set, it takes precedence over the tag. | +| `cardanoTools.image.repository` | `""` | Overrides the cardano-tools utility image used for artifact staging containers. Empty uses the operator's built-in versioned reference. | +| `cardanoTools.image.tag` | `""` | Tag for the cardano-tools override. Applied only when `repository` is set. | +| `cardanoTools.image.digest` | `""` | Digest for the cardano-tools override. When set, it takes precedence over the tag. | +| `imagePullSecrets` | `[]` | Image pull secrets added to the manager pod. | + +The `cardanoTestnet` and `cardanoTools` overrides only render a flag when +`repository` is set; otherwise the operator keeps its built-in +`:-yacd.N` reference. These exist to run pre-release publisher +changes that the published tags do not yet contain. + +### Manager runtime + +| Key | Default | Meaning | +| --- | --- | --- | +| `replicaCount` | `1` | Number of manager replicas. | +| `manager.logFormat` | `json` | Log output format. Rendered into `--log-format`. One of `json`, `text`. | +| `manager.logLevel` | `info` | Minimum log level. Rendered into `--log-level`. One of `debug`, `info`, `warn`, `error`. | +| `manager.enableHTTP2` | `false` | Enable HTTP/2 for the metrics and webhook servers. Rendered into `--enable-http2` when `true`. | +| `manager.healthProbe.port` | `8081` | Health/readiness probe port. Rendered into `--health-probe-bind-address=:`. | +| `manager.extraArgs` | `[]` | Extra raw arguments appended verbatim to the manager command line. | +| `leaderElection.enabled` | `true` | Enable controller-runtime leader election. Renders `--leader-elect` when `true`. | + +!!! note + The chart enables leader election by default (`leaderElection.enabled: true`), + while the manager binary's `--leader-elect` flag defaults to `false`. The + chart adds the flag for you. + +### Metrics and TLS + +| Key | Default | Meaning | +| --- | --- | --- | +| `metrics.enabled` | `true` | Serve the metrics endpoint. When `false`, the chart renders `--metrics-bind-address=0` to disable it. | +| `metrics.port` | `8443` | Metrics container port. Rendered into `--metrics-bind-address=:`. | +| `metrics.secure` | `true` | Serve metrics over HTTPS with Kubernetes authn/authz. Rendered into `--metrics-secure=`. | +| `metrics.service.create` | `true` | Create the metrics Service. | +| `metrics.service.port` | `8443` | Metrics Service port. | +| `metrics.service.annotations` | `{}` | Annotations on the metrics Service. | +| `metrics.tls.secretName` | `""` | Secret holding metrics server TLS material. When set, the chart mounts it and renders the `--metrics-cert-*` flags. | +| `metrics.tls.certPath` | `/certs/metrics` | Mount path for the metrics TLS Secret. Rendered into `--metrics-cert-path` when `secretName` is set. | +| `metrics.tls.certName` | `tls.crt` | Certificate filename. Rendered into `--metrics-cert-name`. | +| `metrics.tls.keyName` | `tls.key` | Private key filename. Rendered into `--metrics-cert-key`. | +| `webhook.tls.secretName` | `""` | Secret holding webhook server TLS material. When set, the chart mounts it and renders the `--webhook-cert-*` flags. | +| `webhook.tls.certPath` | `/certs/webhook` | Mount path for the webhook TLS Secret. Rendered into `--webhook-cert-path` when `secretName` is set. | +| `webhook.tls.certName` | `tls.crt` | Certificate filename. Rendered into `--webhook-cert-name`. | +| `webhook.tls.keyName` | `tls.key` | Private key filename. Rendered into `--webhook-cert-key`. | + +### RBAC and service account + +| Key | Default | Meaning | +| --- | --- | --- | +| `serviceAccount.create` | `true` | Create the manager ServiceAccount. | +| `serviceAccount.name` | `""` | ServiceAccount name. Empty derives the name from the release. | +| `serviceAccount.annotations` | `{}` | Annotations on the ServiceAccount. | +| `rbac.create` | `true` | Create the manager Role/RoleBinding and metrics auth RBAC. | +| `rbac.metricsReader.create` | `true` | Create the metrics-reader ClusterRole for scraping the protected metrics endpoint. | + +### Scheduling and resources + +| Key | Default | Meaning | +| --- | --- | --- | +| `resources.requests.cpu` | `10m` | Manager CPU request. (No limits set by default.) | +| `resources.requests.memory` | `64Mi` | Manager memory request. | +| `nodeSelector` | `{}` | Manager pod node selector. | +| `tolerations` | `[]` | Manager pod tolerations. | +| `affinity` | `{}` | Manager pod affinity. | +| `topologySpreadConstraints` | `[]` | Manager pod topology spread constraints. | +| `extraVolumes` | `[]` | Extra volumes added to the manager pod. | +| `extraVolumeMounts` | `[]` | Extra volume mounts added to the manager container. | + +### Security contexts + +| Key | Default | Meaning | +| --- | --- | --- | +| `podSecurityContext.runAsNonRoot` | `true` | Require the pod to run as a non-root user. | +| `podSecurityContext.seccompProfile.type` | `RuntimeDefault` | Pod seccomp profile. | +| `containerSecurityContext.allowPrivilegeEscalation` | `false` | Disallow privilege escalation. | +| `containerSecurityContext.capabilities.drop` | `[ALL]` | Linux capabilities dropped. | +| `containerSecurityContext.readOnlyRootFilesystem` | `true` | Mount the root filesystem read-only. | +| `containerSecurityContext.runAsNonRoot` | `true` | Require the container to run as a non-root user. | +| `containerSecurityContext.runAsUser` | `65532` | Container UID. | + +### Labels and annotations + +| Key | Default | Meaning | +| --- | --- | --- | +| `nameOverride` | `""` | Override the chart name component of generated names. | +| `fullnameOverride` | `""` | Override the full resource name prefix. | +| `commonLabels` | `{}` | Labels added to all chart resources. | +| `commonAnnotations` | `{}` | Annotations added to all chart resources. | +| `deploymentAnnotations` | `{}` | Annotations on the manager Deployment. | +| `podLabels` | `{}` | Labels on the manager pod. | +| `podAnnotations` | `{}` | Annotations on the manager pod. | + +`commonLabels` and `podLabels` must not set the chart's reserved label keys +(`app.kubernetes.io/name`, `app.kubernetes.io/instance`, +`app.kubernetes.io/managed-by`, `app.kubernetes.io/version`, `helm.sh/chart`, +`control-plane`); the chart fails rendering if they do. + +### Kyverno image verification + +This optional block renders a [Kyverno](https://kyverno.io) image-verification +policy that requires the operator images to carry a verifiable Sigstore +signature and SLSA provenance attestation. It is disabled by default and +requires Kyverno to be installed in the cluster. + +| Key | Default | Meaning | +| --- | --- | --- | +| `kyverno.imageVerification.enabled` | `false` | Render the image-verification policy. | +| `kyverno.imageVerification.name` | `""` | Policy name. Empty derives the name from the release. | +| `kyverno.imageVerification.validationFailureAction` | `Enforce` | Policy action when verification fails (`Enforce` or `Audit`). | +| `kyverno.imageVerification.webhookTimeoutSeconds` | `30` | Admission webhook timeout for the policy. | +| `kyverno.imageVerification.imageReferences` | `[]` | Image reference globs the policy applies to. Empty falls back to the manager and faucet repositories (each with `:*` and `@*`). | +| `kyverno.imageVerification.attestor.issuer` | `https://token.actions.githubusercontent.com` | Keyless OIDC issuer for the signing identity. | +| `kyverno.imageVerification.attestor.subject` | `""` | Exact OIDC subject (certificate identity). | +| `kyverno.imageVerification.attestor.subjectRegExp` | `^https://github\.com/meigma/yacd/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$` | OIDC subject regular expression. | +| `kyverno.imageVerification.attestor.rekor.url` | `https://rekor.sigstore.dev` | Rekor transparency log URL. | +| `kyverno.imageVerification.attestation.type` | `https://slsa.dev/provenance/v1` | Predicate type of the required attestation. | +| `kyverno.imageVerification.attestation.buildType` | `https://actions.github.io/buildtypes/workflow/v1` | Expected build type in the provenance. | + +## Manager flags + +Flags accepted by the manager binary, from `cmd/options.go`. The chart sets the +relevant flags for you; configure these directly only when running the manager +outside the chart. + +| Flag | Default | Meaning | +| --- | --- | --- | +| `--metrics-bind-address` | `0` | Metrics endpoint bind address. `0` disables the metrics server. The chart sets `:` when metrics are enabled. | +| `--health-probe-bind-address` | `:8081` | Liveness/readiness probe bind address. | +| `--leader-elect` | `false` | Enable controller-runtime leader election. | +| `--metrics-secure` | `true` | Serve metrics over HTTPS with Kubernetes authn/authz. Negatable as `--no-metrics-secure`. | +| `--enable-http2` | `false` | Enable HTTP/2 for the metrics and webhook servers. Off by default to avoid the HTTP/2 rapid-reset CVEs. | +| `--log-format` | `json` | Log output format. One of `json`, `text`. | +| `--log-level` | `info` | Minimum log level. One of `debug`, `info`, `warn`, `error`. | +| `--metrics-cert-path` | `""` (unset) | Directory holding the metrics server certificate material. Empty disables the certificate watcher. | +| `--metrics-cert-name` | `tls.crt` | Metrics certificate filename within `--metrics-cert-path`. | +| `--metrics-cert-key` | `tls.key` | Metrics private key filename within `--metrics-cert-path`. | +| `--webhook-cert-path` | `""` (unset) | Directory holding the webhook server certificate material. Empty disables the certificate watcher. | +| `--webhook-cert-name` | `tls.crt` | Webhook certificate filename within `--webhook-cert-path`. | +| `--webhook-cert-key` | `tls.key` | Webhook private key filename within `--webhook-cert-path`. | +| `--default-faucet-image` | `ghcr.io/meigma/yacd/faucet:dev` | Faucet image used when a CardanoNetwork does not set `spec.chainAPI.faucet.image`. The chart renders this from `faucet.image.*`. | +| `--default-cardano-testnet-image` | `""` | Override the cardano-testnet image used for the create-env init container, the faucet source-address init container, and the default cardano-node container. Empty uses the built-in versioned reference. | +| `--default-cardano-tools-image` | `""` | Override the cardano-tools image used for artifact staging containers. Empty uses the built-in versioned reference. | diff --git a/docs/reference/environment.md b/docs/reference/environment.md new file mode 100644 index 00000000..d6e45ed2 --- /dev/null +++ b/docs/reference/environment.md @@ -0,0 +1,118 @@ +# Environment file + +The environment file is the developer-facing YACD configuration document you +pass to the CLI with `-f/--file` (see [CLI reference](cli.md)). It is a single +YAML document with a fixed `apiVersion`/`kind` envelope and one field of +substance: `spec.network`, a Cardano network specification. + +The CLI loads this file, validates it, and renders it into a `CardanoNetwork` +object that the operator reconciles. The document does **not** carry a name or +namespace. Identity is supplied on the command line (`yacd up NAME -n +NAMESPACE`), so one file can deploy under many names and namespaces. + +## Document shape + +```yaml +apiVersion: yacd.meigma.io/devconfig/v1alpha1 +kind: Environment +spec: + network: {} # a CardanoNetworkSpec +``` + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `apiVersion` | string | yes | Must equal `yacd.meigma.io/devconfig/v1alpha1`. | +| `kind` | string | yes | Must equal `Environment`. | +| `spec.network` | object | yes | A `CardanoNetworkSpec`. Decoded directly into the API type, so the document exposes the same fields the CRD exposes. See the [CardanoNetwork reference](cardanonetwork.md) for every network field, type, enum, and default. | + +The envelope is intentionally thin. `spec.network` is the only field today; +the wrapper exists so future top-level fields can be added without breaking +existing documents. + +For complete, copy-paste files per mode and profile, see the +[recipes](../recipes.md). + +## Validation + +The CLI rejects an invalid file before contacting the cluster. Validation runs +in three layers. + +### Strict decoding (unknown fields rejected) + +The file is parsed with a strict YAML decoder. Any key the schema does not +recognize is an error. A misspelled or misplaced field fails the load rather +than being silently ignored. The decoder names the offending key by its leaf +field name, not its full path. + +``` +parse developer config: error unmarshaling JSON: while decoding JSON: json: unknown field "nde" +``` + +### Envelope and shape checks + +After decoding, the document is checked for envelope integrity and structural +consistency: + +- `apiVersion` must equal `yacd.meigma.io/devconfig/v1alpha1`. +- `kind` must equal `Environment`. +- `spec.network.node.version` must be set. +- `spec.network.node.port` must be greater than 0. +- `spec.network.mode` must be `local` or `public`. +- In `local` mode, `spec.network.local` is required and `spec.network.public` + must be absent. +- In `public` mode, `spec.network.public` is required and `spec.network.local` + must be absent. `spec.network.public.profile` must be `preview`, `preprod`, + or `mainnet`. A `bootstrap` block is allowed only for `mainnet`, where + `bootstrap.mithril` is required. + +Runtime-support checks also run here (supported eras, supported Ogmios/Kupo +images and version pairings, port-conflict detection, mainnet storage minimums, +and which chain APIs are allowed per mode). Those constraints belong to the +network spec; see the [CardanoNetwork reference](cardanonetwork.md). + +### Explicit-field enforcement + +Some network fields have CRD defaults. On a strongly-typed Go value the +decoder cannot tell whether the author wrote `port: 0` or omitted `port` +entirely, and a silently-defaulted value here would produce surprising runtime +behavior (for example, an unset `node.port` rendering a Service with port 0). +To prevent that, the CLI re-reads the raw YAML and requires these paths to be +written explicitly. A missing path fails with: + +``` +spec.network.node.port must be set explicitly in developer config +``` + +**Always required:** + +- `spec.network.mode` +- `spec.network.node.version` +- `spec.network.node.port` + +**Required when `mode: local`:** + +- `spec.network.local.networkMagic` +- `spec.network.local.era` +- `spec.network.local.timing.slotLength` +- `spec.network.local.timing.epochLength` +- `spec.network.local.topology.pools.count` + +**Required when `mode: public`:** + +- `spec.network.public.profile` + +**Required only when the parent block is present:** + +| If you include... | You must also set explicitly | +| --- | --- | +| `spec.network.node.storage` | `spec.network.node.storage.size` | +| `spec.network.local.genesis` | `spec.network.local.genesis.profile` | +| `spec.network.chainAPI.ogmios` | `...ogmios.enabled`, `...ogmios.image`, `...ogmios.port` | +| `spec.network.chainAPI.kupo` | `...kupo.enabled`, `...kupo.image`, `...kupo.port` | +| `spec.network.chainAPI.faucet` | `...faucet.enabled`, `...faucet.port`, `...faucet.defaultSource`, `...faucet.minTopUpLovelace`, `...faucet.maxTopUpLovelace` | + +These rules cover only *presence* in the source. The accepted values, types, +and defaults for each field are documented in the +[CardanoNetwork reference](cardanonetwork.md). Chain-API blocks you omit +entirely keep their network-spec defaults; you opt into explicit-field +enforcement for a block only by including that block. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..eeb67972 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,263 @@ +# Troubleshooting + +How to diagnose and fix the most common problems with a `CardanoNetwork` or +`CardanoDBSync`. Start by reading the status, then match your symptom to a +failure mode below. + +## Start here: read the status + +For a network, run [`yacd info`](reference/cli.md) with the network name. It +fetches the `CardanoNetwork` and prints its conditions, network identity, and +endpoints: + +```sh +yacd info my-net +``` + +The output has a `Conditions:` section with one line per condition, formatted as +`Type: Status (Reason) - Message`: + +``` +Conditions: + Ready: False (Progressing) - waiting for the primary node to synchronize + NodeReady: True (NodeRunning) - primary node container is running + NodeSynchronized: False (Syncing) - node is catching up to the inferred tip +``` + +Read it top-down: + +- **`Ready`** is the rollup. `Ready: True` means the network is usable through + its published endpoints. `Ready: False` means it is not, and the per-component + conditions tell you which part is blocking. +- **`Degraded: True`** means the resource failed to reach or maintain its + desired state. Read its `Message` first; it carries the specific error. +- **`Progressing`** means the resource is still being created or updated. A + `False` rollup with `Progressing: True` is normal during initial bring-up. + +The per-component conditions a `CardanoNetwork` publishes are `NodeReady` +(primary node container running), `NodeSynchronized` (caught up to the inferred +network tip), `NodeProgressing` (tip advancing or already synchronized), +`ArtifactsReady` (artifact bundle staged and served over HTTP), `OgmiosReady`, +`KupoReady`, `FaucetReady`, `WalletReady`, and `DBSyncAttachmentReady` (a +primary-sidecar db-sync attachment is not blocking the primary Pod). See the +[CardanoNetwork reference](reference/cardanonetwork.md) for the full list. + +For machine-readable status, including the `reason` and `message` of every +condition, add `--json`: + +```sh +yacd info my-net --json +``` + +For a `CardanoDBSync`, `yacd info` does not apply. Read its conditions directly: + +```sh +kubectl get cardanodbsync my-dbsync -o jsonpath='{.status.conditions}' | jq +``` + +Its component conditions are `Ready`, `FollowerNodeReady`, `NodeSocketReady`, +`SidecarMaterialReady`, `PostgresReady`, `DBSyncReady`, and `Synced`. See the +[CardanoDBSync reference](reference/cardanodbsync.md). + +## Network never reaches Ready + +**Symptom.** `yacd info` shows `Ready: False` and stays there. + +**Cause.** Look at which child condition is `False`. The common cases: + +- `NodeReady: False` — the primary node container is not running yet. Check the + node Pod with `kubectl describe pod` for image pull or scheduling errors (see + [image pull issues](#image-pull-issues) and [storage rejected](#pvc-too-small-or-storage-rejected)). +- `NodeSynchronized: False` with `NodeProgressing: True` — the node is running + but still catching up to the tip. This is expected on public profiles and + resolves on its own once the node syncs. The `sync` status sub-resource + reports `lagSlots` and `lagSeconds` so you can watch progress. +- `OgmiosReady: False` or `KupoReady: False` — a chain API sidecar is enabled + but not yet connected or synchronized. Ogmios and Kupo are enabled by default. + +**Fix.** Wait out a genuine sync, or fix the blocking child condition using the +matching section below. To watch live: + +```sh +kubectl get cardanonetwork my-net -w +``` + +## Mainnet stuck without a Mithril bootstrap + +!!! warning "Mainnet requires a Mithril bootstrap" + Mainnet is too large to sync from genesis in a development context. The + `CardanoNetwork` CRD validation rejects a mainnet network that has no + Mithril bootstrap, so the API server refuses the resource before it ever + reaches the controller. + +**Symptom.** Applying a `public` network with `spec.public.profile: mainnet` +fails immediately with: + +``` +bootstrap.mithril is required only when public.profile is mainnet +``` + +**Cause.** The CRD enforces that `spec.public.bootstrap.mithril` is present +exactly when (and only when) the profile is mainnet. A mainnet network with no +bootstrap, or a preprod/preview network that sets a bootstrap, is invalid. + +**Fix.** Add a Mithril bootstrap block. The image and snapshot have defaults, so +an empty block is enough to satisfy the rule: + +```yaml +spec: + mode: public + public: + profile: mainnet + bootstrap: + mithril: {} +``` + +`mithril.snapshot` defaults to `latest` and `mithril.image` defaults to the +pinned `mithril-client` image. See the +[CardanoNetwork reference](reference/cardanonetwork.md) for the bootstrap +fields. Mainnet also needs large persistent storage and the host-only opt-in to +create it; see the [recipes](recipes.md) for a complete mainnet manifest. + +## PVC too small or storage rejected + +**Symptom.** `NodeReady: False`, and the node Pod is stuck `Pending`. A +`kubectl describe pod` shows the PVC is not bound, or events report a +provisioning failure. + +**Cause.** The node database PVC requested by `spec.node.storage.size` could not +be provisioned. Typical reasons: the requested size exceeds what the cluster can +provision, the named `spec.node.storage.storageClassName` does not exist, or the +default StorageClass cannot bind the claim. The node storage size defaults to +`10Gi`, which is fine for local devnets but far too small for a public profile. + +**Fix.** Inspect the claim and its events: + +```sh +kubectl get pvc -l app.kubernetes.io/instance=my-net +kubectl describe pvc +``` + +Then set a size and StorageClass the cluster can satisfy: + +```yaml +spec: + node: + storage: + size: 200Gi + storageClassName: standard +``` + +A PVC's requested size is immutable after creation. If you need to grow it, +either use a StorageClass with `allowVolumeExpansion: true` or recreate the +network. See [`spec.node.storage`](reference/cardanonetwork.md) for the fields. + +## External Postgres unreachable (db-sync) + +**Symptom.** `CardanoDBSync` shows `PostgresReady: False` or `DBSyncReady: +False`, and `Ready` never becomes `True`. The db-sync container logs report +connection refused, authentication failure, or TLS errors against the Postgres +host. + +**Cause.** With an external database, db-sync connects to the host you supplied +in `spec.database.external`. The connection fails when the host or port is +wrong, the password Secret is missing or holds the wrong key, the database or +user does not exist, or `sslMode` does not match the server's TLS posture. + +**Fix.** Verify each field against the running Postgres: + +- `spec.database.external.host` and `port` (default `5432`) resolve from inside + the cluster. +- `spec.database.external.database` (default `cexplorer`) and `user` (default + `postgres`) exist on the server. +- `spec.database.external.passwordSecretRef` names a same-namespace Secret, and + the referenced `key` (default `password`) holds the password. +- `spec.database.external.sslMode` (default `disable`) matches the server. Use + `require`, `verify-ca`, or `verify-full` only if the server presents TLS. + +Confirm the Secret exists and has the expected key: + +```sh +kubectl get secret -o jsonpath='{.data}' | jq 'keys' +``` + +Note that `spec.database` must set exactly one of `external` or `managed`; +setting both, or neither, is rejected at admission with +`exactly one of database.external or database.managed must be set`. See the +[CardanoDBSync reference](reference/cardanodbsync.md) for the database fields. + +## Faucet on a public network or faucet without Kupo + +!!! warning "The faucet is local-only" + The faucet spends from generated devnet UTxOs, so it only makes sense on a + `local` network. Its top-up endpoint and the bearer token it requires are + host-only. + +**Symptom.** A network with `spec.chainAPI.faucet.enabled: true` never publishes +a faucet endpoint (`yacd info` shows `faucet: unavailable`), `FaucetReady` does +not become `True`, or `WalletReady: False` for a network that enables the +developer wallet. + +**Cause.** Two related misconfigurations: + +- The faucet is enabled on a `public` network. The faucet has no generated + source UTxOs to spend on a public profile, so it cannot serve top-ups. +- The developer wallet is enabled without the faucet (and Kupo). The wallet is + bootstrapped by funding it through the faucet, so `spec.chainAPI.wallet` + requires both `spec.chainAPI.faucet` and `spec.chainAPI.kupo` to be enabled, + and is supported in local mode only. `wallet.fundingLovelace` must also not + exceed the faucet's `maxTopUpLovelace` (default `10000000000`). + +**Fix.** Use the faucet and wallet only on a local network, and enable Kupo +alongside the wallet: + +```yaml +spec: + mode: local + chainAPI: + kupo: + enabled: true + faucet: + enabled: true + wallet: + enabled: true + fundingLovelace: 100000000000 +``` + +To submit a top-up against a running local faucet, use +[`yacd topup`](reference/cli.md). Kupo is required for the `--await` flag, which +waits for the funding transaction to confirm on-chain. + +## Image pull issues + +**Symptom.** A Pod is stuck `ImagePullBackOff` or `ErrImagePull`, and the owning +condition (`NodeReady`, `OgmiosReady`, `KupoReady`, `FaucetReady`, or +`PostgresReady`/`DBSyncReady`) stays `False`. + +**Cause.** Kubernetes could not pull a container image. The reference is wrong, +the tag or digest does not exist, or the registry needs credentials the cluster +does not have. + +**Fix.** Identify the failing image from the Pod events: + +```sh +kubectl describe pod +``` + +Then check the override on the spec. Each component has a pinned default image, +so a pull failure usually means a custom override is wrong: + +- `spec.node.image` / `spec.node.version` (the controller derives the node image + from `version`, default `11.0.1`, when `image` is unset). +- `spec.chainAPI.ogmios.image` and `spec.chainAPI.kupo.image` (pinned defaults). +- `spec.chainAPI.faucet.image` — an override must use the **same repository** as + the controller's configured default faucet image; only the tag or digest may + vary. +- `spec.public.bootstrap.mithril.image` for the Mithril client. +- For db-sync: `spec.image` and `spec.database.managed.image` (default + `postgres:17.2-alpine`). + +If the image is private, add an `imagePullSecret` to the namespace. See the +[CardanoNetwork reference](reference/cardanonetwork.md) and +[CardanoDBSync reference](reference/cardanodbsync.md) for the exact field paths +and pinned default tags. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..8d435fcb --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,58 @@ +site_name: yacd +site_description: Kubernetes-native Cardano development environments +repo_url: https://github.com/meigma/yacd +repo_name: meigma/yacd +docs_dir: docs + +theme: + name: material + features: + - navigation.top + - navigation.footer + - navigation.tracking + - toc.follow + - content.code.copy + - content.code.annotate + - search.suggest + - search.highlight + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - tables + - toc: + permalink: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + +plugins: + - search + +nav: + - Home: index.md + - Developer Guide: + - Getting started: developer/getting-started.md + - Defining networks: developer/networks.md + - Funding accounts: developer/funding.md + - Connecting tools & tests: developer/connecting-tools.md + - Operator Guide: + - Installation: operator/installation.md + - DB-Sync indexing: operator/db-sync.md + - Testing & CI: operator/testing.md + - Reference: + - CLI: reference/cli.md + - CardanoNetwork: reference/cardanonetwork.md + - CardanoDBSync: reference/cardanodbsync.md + - Environment file: reference/environment.md + - Configuration: reference/configuration.md + - Recipes: recipes.md + - Troubleshooting: troubleshooting.md + - Concepts: + - Architecture: concepts/architecture.md + - Security model: concepts/security.md diff --git a/moon.yml b/moon.yml index 14442d55..7b9831fa 100644 --- a/moon.yml +++ b/moon.yml @@ -184,3 +184,17 @@ tasks: options: cache: false runInCI: true + + docs: + command: 'uv run --with mkdocs-material==9.7.6 mkdocs build --strict' + inputs: + - 'docs/**/*' + - 'mkdocs.yml' + options: + cache: false + + docs-serve: + command: 'uv run --with mkdocs-material==9.7.6 mkdocs serve' + options: + cache: false + runInCI: false From 69190132bcfb522bcc917e223edeebbbe1867057 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Tue, 2 Jun 2026 19:18:54 -0700 Subject: [PATCH 2/9] docs: note Docker prerequisite and k3d caching for devnet State that `yacd devnet` needs Docker running, and that the CLI downloads and caches a pinned, checksum-verified k3d binary on first use (under ~/.local/share/yacd/bin), so the slower first run is not surprising. Applied to the getting-started tutorial, the Home teaser, and the CLI reference. Link k3d and Docker at their first mention on each page. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/developer/getting-started.md | 11 +++++++---- docs/index.md | 3 ++- docs/operator/installation.md | 2 +- docs/reference/cli.md | 5 +++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index 225e38b1..303cd8a2 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -9,10 +9,13 @@ By the end you will have run a complete local development loop. For *why* the pieces fit together this way, see [Architecture](../concepts/architecture.md). -!!! note "What you need" - A working [Docker](https://www.docker.com/) (or compatible container - runtime) so the CLI can create a local [k3d](https://k3d.io) cluster. - Everything else, including k3d itself, is provisioned for you. +!!! note "Prerequisites" + `yacd devnet` needs a running [Docker](https://www.docker.com/) (or a + compatible container runtime), because [k3d](https://k3d.io) runs the local + cluster as containers. You do not install k3d yourself: on first run the CLI + downloads a pinned, checksum-verified k3d binary and caches it under + `~/.local/share/yacd/bin`, so the first `yacd devnet` is slower than later + ones. ## 1. Install the CLI diff --git a/docs/index.md b/docs/index.md index ee511315..8a109567 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,7 +30,8 @@ yacd ships as two surfaces: ## Try it in one command A single `yacd devnet` provisions a local cluster, installs the operator, and -applies a funded network: +applies a funded network. It needs [Docker](https://www.docker.com/) running and +fetches a pinned [k3d](https://k3d.io) binary on first use: ```sh yacd devnet diff --git a/docs/operator/installation.md b/docs/operator/installation.md index 43a0cefb..da591087 100644 --- a/docs/operator/installation.md +++ b/docs/operator/installation.md @@ -2,7 +2,7 @@ Install the YACD operator on a remote Kubernetes cluster with [Helm](https://helm.sh) from the OCI chart published to GitHub Container Registry. The chart deploys the controller manager, its RBAC and ServiceAccount, a secured metrics Service, and the `CardanoNetwork` and `CardanoDBSync` CRDs. -This page covers a remote install. For a local k3d cluster, the `yacd` CLI manages its own cluster lifecycle; see the [CLI reference](../reference/cli.md). For every chart value and manager flag, see the [configuration reference](../reference/configuration.md). +This page covers a remote install. For a local [k3d](https://k3d.io) cluster, the `yacd` CLI manages its own cluster lifecycle; see the [CLI reference](../reference/cli.md). For every chart value and manager flag, see the [configuration reference](../reference/configuration.md). ## Prerequisites diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 522b0350..f9820bd5 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -66,6 +66,11 @@ Brings a managed [k3d](https://k3d.io) cluster, the operator, and a default funded local network to a ready state in one command. Takes no `NAME` and no `--namespace` (it manages a fixed `devnet` network in a `devnet` namespace). +`devnet` requires a running [Docker](https://www.docker.com/) (or compatible +container runtime). It does not require a preinstalled k3d: on first use the CLI +downloads a version-pinned, SHA256-verified k3d binary and caches it under +`$XDG_DATA_HOME/yacd/bin` (default `~/.local/share/yacd/bin`). + | Flag | Type | Default | Meaning | | --- | --- | --- | --- | | `--bare` | bool | `false` | Stop after installing the operator; apply no network. | From 3624a22a78cb298847b3eadb6c052a056937be96 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Tue, 2 Jun 2026 19:50:09 -0700 Subject: [PATCH 3/9] docs: add light/dark palette toggle defaulting to system preference Enable Material's automatic color mode: the site follows the operating system's light/dark preference by default, with a header toggle to switch between auto, light (default scheme), and dark (slate scheme). No custom colors. Co-Authored-By: Claude Opus 4.8 (1M context) --- mkdocs.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 8d435fcb..e442252f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,6 +6,24 @@ docs_dir: docs theme: name: material + palette: + # Automatic mode follows the operating system preference (the default). + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + # Light mode. + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Dark mode. + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference features: - navigation.top - navigation.footer From b09dfdd8f6c397a0680151690b02b0a4e03b123a Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Tue, 2 Jun 2026 19:51:02 -0700 Subject: [PATCH 4/9] docs: move the "keep these handy" aside into a note callout In the getting-started tutorial, the reminder to keep the wallet address and network magic (and that the Try: commands are the next steps) reads better as a Material note admonition than as inline prose. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/developer/getting-started.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index 303cd8a2..6a16289f 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -75,9 +75,10 @@ Try: yacd devnet down ``` -The `Wallet` line is a pre-funded developer address, and the network uses -network magic `42`. Note both: you will use them in the steps below. The two -commands under `Try:` are exactly the next two things you will run. +!!! note "Keep these handy" + The `Wallet` line is a pre-funded developer address, and the network uses + network magic `42`. You will use both in the steps below, and the two + commands under `Try:` are exactly the next two things you will run. For what the cluster, operator, and network are and how they relate, see [Architecture](../concepts/architecture.md). From bf7fd98960c285247d8726987ae3d1066bca97dc Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 3 Jun 2026 12:24:43 -0700 Subject: [PATCH 5/9] docs: adopt session 057 CLI changes (init, self-forwarding topup, all-namespaces list) Align the docs branch with PR #93 (merged): `yacd list` now defaults to all namespaces (`-A` removed); `yacd topup NAME LOVELACE` takes a positional lovelace and self-forwards the faucet/Kupo, so the `yacd run` wrapper and `--faucet-url` are no longer needed; and the new `yacd init` prints a commented, ready-to-run environment template. Make `yacd init > yacd.yaml` the default starting point in the "Defining networks" guide (scaffold, edit, `yacd up`), add an `init` reference section and Commands-table entry, and revert the now-stale `list -A` / topup-under-`run` examples added earlier. Verified: mkdocs build --strict clean; `yacd init` -> `yacd up --dry-run` renders a valid CardanoNetwork; init/topup/list `--help` match the docs. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/developer/connecting-tools.md | 20 ++++---- docs/developer/funding.md | 53 ++++++++++----------- docs/developer/getting-started.md | 28 ++++++----- docs/developer/networks.md | 44 +++++++++++------- docs/recipes.md | 5 ++ docs/reference/cli.md | 74 ++++++++++++++++++++---------- docs/troubleshooting.md | 5 +- 7 files changed, 130 insertions(+), 99 deletions(-) diff --git a/docs/developer/connecting-tools.md b/docs/developer/connecting-tools.md index 3542c563..25dfb488 100644 --- a/docs/developer/connecting-tools.md +++ b/docs/developer/connecting-tools.md @@ -130,21 +130,21 @@ names and shape, see the [endpoints.json schema](../reference/cli.md). ## Fund an address from a test -`yacd topup NAME --address ADDR --lovelace N --await` funds `ADDR` through the -faucet and polls Kupo until the funding transaction's output appears, so a test -never races chain inclusion. `--await` requires a Kupo URL: pass `--kupo-url`, -or run under `yacd run`, which sets `YACD_KUPO_URL` (the default `--kupo-url` -source): +`yacd topup NAME LOVELACE --address ADDR --await` funds `ADDR` through the faucet +and polls Kupo until the funding transaction's output appears, so a test never +races chain inclusion. `topup` self-forwards the faucet and Kupo on its own, so +it needs no `yacd run` wrapper and no URL flags: ```sh export ADDR=addr_test1... # the address your test funds -yacd run my-net -- sh -c \ - 'yacd topup my-net --address "$ADDR" --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL" --await' +yacd topup my-net 1000000 --address "$ADDR" --await ``` -The loopback faucet URL from a `run` or `connect` forward is exempt from the -`topup` trust gate, so no `--trust-faucet-url` flag is needed against it. See -the [CLI reference](../reference/cli.md) for all `topup` flags. +Run inside `yacd run`, `topup` instead inherits the ambient `YACD_FAUCET_URL` +and `YACD_KUPO_URL` and reuses those forwards rather than opening a second one. +Either way the loopback faucet URL is exempt from the `topup` trust gate, so no +`--trust-faucet-url` is needed. See the [CLI reference](../reference/cli.md) for +all `topup` flags. ## See also diff --git a/docs/developer/funding.md b/docs/developer/funding.md index ebcfeefe..7094d4c8 100644 --- a/docs/developer/funding.md +++ b/docs/developer/funding.md @@ -64,27 +64,27 @@ yacd info my-net --json ## 3. Send lovelace -The faucet is a cluster-internal Service, so run `topup` through `yacd run`, -which forwards the faucet to a local port and exposes it as `$YACD_FAUCET_URL`: +Fund an address with an exact lovelace amount. `topup` reaches the faucet on its +own — with no `--faucet-url` it opens a short-lived port-forward, POSTs, and +tears it down — so it works directly from your host with no `yacd run` wrapper: ```sh -yacd run my-net -- sh -c \ - 'yacd topup my-net --address addr_test1... --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL"' +yacd topup my-net 1000000 --address addr_test1... ``` -`--address` and `--lovelace` are both required, and `--lovelace` must be greater -than zero and within the faucet's configured min/max bounds. `topup` reads the -auth token from the published Secret automatically, and the loopback URL that -`run` exposes is exempt from the [trust gate](../concepts/security.md). On -success it prints the transaction ID, source, lovelace, and destination. Add -`--json` for a machine-readable result. +`LOVELACE` is a positional argument, `--address` is required, and the amount must +be greater than zero and within the faucet's configured min/max bounds. `topup` +reads the auth token from the published Secret automatically, and the +self-forwarded loopback URL is exempt from the +[trust gate](../concepts/security.md). On success it prints the transaction ID, +source, lovelace, and destination. Add `--json` for a machine-readable result. -!!! note "Running `topup` without `run`" - Without `--faucet-url`, `topup` targets the faucet URL the cluster published - (`http://-faucet..svc.cluster.local:`). That name - resolves only from inside the cluster, so a bare `yacd topup` works for - in-cluster callers (such as a CI job running in a Pod) but not from your - host. From your host, bridge it with `yacd run` as shown above. +!!! note "Inside `yacd run`, or with an override" + `topup` honors an ambient `YACD_FAUCET_URL` (set inside `yacd run`), so it + works unchanged there without opening a second forward. An explicit + `--faucet-url` suppresses self-forwarding; a custom non-loopback value then + requires `--trust-faucet-url` (and `--allow-insecure-faucet-url` for + `http://`). The full `topup` flag set — including `--source`, `--faucet-url`, the `--trust-faucet-url` / `--allow-insecure-faucet-url` trust gates, and the @@ -98,22 +98,15 @@ until the funding transaction is actually confirmed on-chain, add `--await`, which polls [Kupo](https://cardanosolutions.github.io/kupo/) for the new output: ```sh -yacd topup my-net --address addr_test1... --lovelace 1000000 --await +yacd topup my-net 1000000 --address addr_test1... --await ``` -`--await` requires a Kupo URL. Pass `--kupo-url`, or run under `yacd run`, which -sets `YACD_KUPO_URL` automatically: - -```sh -yacd run my-net -- sh -c \ - 'yacd topup my-net --address "$ADDR" --lovelace 1000000 --faucet-url "$YACD_FAUCET_URL" --await' -``` - -The loopback faucet URL exposed by `run` is exempt from the trust gate, so no -`--trust-faucet-url` is needed there. When the output appears, `topup` prints -`Confirmed on-chain.` and exits. See -[Host access and the `YACD_*` contract](connecting-tools.md) for how `run` -bridges the cluster-internal endpoints to your host. +When `topup` self-forwards it reuses that same session's Kupo, so `--await` +needs no extra flags. When the output appears, `topup` prints `Confirmed +on-chain.` and exits. If you override the faucet with `--faucet-url`, supply a +matching `--kupo-url` for `--await`. See +[Connecting tools and tests](connecting-tools.md) for how `run` bridges +cluster-internal endpoints to your host. ## See also diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index 6a16289f..8e95b772 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -85,11 +85,10 @@ For what the cluster, operator, and network are and how they relate, see ## 3. List and inspect the network -List the networks in the cluster. Because `devnet` runs in its own `devnet` -namespace, list across all namespaces with `-A`: +List the networks in the cluster (`yacd list` shows every namespace by default): ```sh -yacd list -A +yacd list ``` The `devnet` network you just created appears in the output. Inspect it in @@ -123,21 +122,19 @@ the chain is producing blocks. ## 5. Fund an address -The faucet runs inside the cluster, so reach it through `yacd run`, which -forwards the faucet to a local port and exposes it as `$YACD_FAUCET_URL`. -Replace `
` with a Cardano testnet address you control (the `Wallet` -address from step 2 works), and `` with the amount to send -(1 ADA = 1,000,000 lovelace): +Use the local faucet to send funds to an address. `yacd topup` reaches the +faucet on its own (it opens a short-lived port-forward), so no `yacd run` +wrapper is needed. Replace `
` with a Cardano testnet address you +control (the `Wallet` address from step 2 works), and `` with the +amount to send (1 ADA = 1,000,000 lovelace): ```sh -yacd run devnet -- sh -c \ - 'yacd topup devnet --address
--lovelace --faucet-url "$YACD_FAUCET_URL"' +yacd topup devnet --address
``` -Both `--address` and `--lovelace` are required, and `--lovelace` must be greater -than zero. On success the faucet submits a funding transaction and `topup` -prints the transaction ID. The loopback faucet URL that `run` exposes is exempt -from the [trust gate](../concepts/security.md), so no extra flags are needed. +`LOVELACE` is a positional argument and `--address` is required. On success the +faucet submits a funding transaction and `topup` prints the transaction ID. Add +`--await` to block until the funds are confirmed on-chain. !!! warning "The faucet is local-only" The faucet and its auth token are part of your local devnet only. Never @@ -160,7 +157,8 @@ start fresh. ## Where to go next -- [Working with networks](networks.md) — apply your own network definitions. +- [Working with networks](networks.md) — scaffold your own config with `yacd + init` and apply it. - [Connecting tools](connecting-tools.md) — wire Ogmios, Kupo, and other tools to a network from your host. - [Funding](funding.md) — the full faucet and top-up workflow. diff --git a/docs/developer/networks.md b/docs/developer/networks.md index 205ae99f..62e72554 100644 --- a/docs/developer/networks.md +++ b/docs/developer/networks.md @@ -12,14 +12,20 @@ For what each Environment field means, see the copy-paste manifests, see [Recipes](../recipes.md). For every command flag and default, see the [CLI reference](../reference/cli.md). -## Write a minimal Environment and apply it +## Scaffold and apply an Environment -An Environment file carries the `apiVersion`/`kind` envelope and a single -`spec.network`. The identity (name and namespace) is supplied on the command -line, not in the file. +The quickest way to start is `yacd init`, which prints a fully-commented +Environment to stdout. Redirect it to a file: -Save the single-pool local manifest from [Recipes](../recipes.md) as -`yacd.yaml`, then apply it: +```sh +yacd init > yacd.yaml +``` + +The generated `yacd.yaml` is a ready-to-run local devnet: a single-pool network +with the faucet and a pre-funded wallet enabled. Its commented blocks document +the rest of the API — chain-API image and port overrides, and a public/mainnet +alternative — so you grow the config by uncommenting a whole block at a time. +Edit it to taste, then apply it: ```sh yacd up dev -f yacd.yaml @@ -27,14 +33,16 @@ yacd up dev -f yacd.yaml `yacd up` renders the Environment into a `CardanoNetwork` named `dev` in namespace `dev`, creates the namespace if needed, server-side-applies the -network, and then waits until it reports `Ready`. Use `--wait=false` to apply -without blocking, and `--timeout` to change the readiness deadline (default -`12m`). See the [CLI reference](../reference/cli.md) for the full flag set. +network, and waits until it reports `Ready`. The identity (name and namespace) +comes from the command line, not the file. Use `--wait=false` to apply without +blocking, and `--timeout` to change the readiness deadline (default `12m`). See +the [CLI reference](../reference/cli.md) for the full flag set. !!! note - `spec.network.mode`, `spec.network.node.version`, and - `spec.network.node.port` must be written explicitly in the file; the loader - rejects a document that omits them. Local mode additionally requires + The `yacd init` template already spells these out, but if you write or trim a + file by hand: `spec.network.mode`, `spec.network.node.version`, and + `spec.network.node.port` must be written explicitly; the loader rejects a + document that omits them. Local mode additionally requires `spec.network.local` (with `networkMagic`, `era`, `timing.slotLength`, `timing.epochLength`, and `topology.pools.count`). See the [Environment file reference](../reference/environment.md) for the required @@ -56,7 +64,7 @@ namespace is created and nothing is applied. ## Inspect running environments -List the `CardanoNetwork` objects in the active namespace: +List every `CardanoNetwork` across all namespaces: ```sh yacd list @@ -67,11 +75,11 @@ The table shows `NAME`, `NAMESPACE`, `MODE`, `READY`, and a comma-separated published yet). `READY` reflects a fresh `Ready` condition, so a stale status is reported as not ready. -List across every namespace with `-A`, or emit machine-readable JSON with +Scope to a single namespace with `-n`, or emit machine-readable JSON with `--json`: ```sh -yacd list -A +yacd list -n dev yacd list --json ``` @@ -108,8 +116,10 @@ Public mode connects a node to a real Cardano network instead of a synthetic local chain. The `preview` and `preprod` test networks need no bootstrap and are the recommended way to develop against public-network data. -Save the preview manifest from [Recipes](../recipes.md) as `yacd.yaml`, then -apply it the same way: +To switch the scaffolded config to a public network, set `spec.network.mode: +public`, remove the `local:` block, and uncomment the `public:` block — the +`yacd init` template marks exactly what to change. Or save the preview manifest +from [Recipes](../recipes.md) as `yacd.yaml`. Then apply it the same way: ```sh yacd up preview -f yacd.yaml diff --git a/docs/recipes.md b/docs/recipes.md index 6ac8d7dd..8b5a77a9 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -4,6 +4,11 @@ Copy-paste manifests for the most common YACD setups. Each recipe is mirrored verbatim from a file under `examples/`. Use them as a starting point and adjust field values to suit your cluster. +!!! tip + `yacd init` prints a commented version of the single-pool local devnet below, + ready to redirect into a file (`yacd init > yacd.yaml`). It is the quickest + way to start a custom network; see [Defining networks](developer/networks.md). + Developer environment files (`kind: Environment`) are applied with the CLI: ```sh diff --git a/docs/reference/cli.md b/docs/reference/cli.md index f9820bd5..f0f5b78f 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -23,11 +23,12 @@ Commands: | `devnet` | Bring up a local Cardano devnet (cluster, operator, and a funded network). | | `devnet down` | Delete the managed devnet cluster. | | `devnet status` | Show the managed devnet cluster, operator, and network status. | +| `init` | Print a commented `yacd.yaml` environment template to stdout. | | `up NAME` | Create or update a YACD environment and wait for readiness. | | `down NAME` | Delete a YACD environment and wait for clean removal. | -| `list` | List YACD environments in the cluster. | +| `list` | List YACD environments across all namespaces (or one with `-n`). | | `info NAME` | Print CardanoNetwork status and connection information. | -| `topup NAME` | Submit a faucet top-up. | +| `topup NAME LOVELACE` | Submit a faucet top-up (self-forwards the faucet). | | `run NAME [-- command ...]` | Run a command (or a shell) on the host with the `YACD_*` environment wired to forwarded endpoints. | | `connect NAME` | Forward a network's endpoints and hold them open until interrupted. | | `exec NAME -- command ...` | Run a command inside the primary node Pod (for socket-bound tools). | @@ -102,6 +103,26 @@ Read-only unified view of the managed cluster, operator, and networks. Takes no flags beyond the [global flags](#global-flags). Prints a one-line hint when no managed cluster exists. +## init + +```text +yacd init +``` + +Prints a fully-commented developer environment template to stdout and takes no +arguments or flags beyond the [global flags](#global-flags). The active +configuration is a ready-to-run local devnet (faucet plus a pre-funded wallet); +commented blocks document the rest of the API, including chain-API overrides and +a public/mainnet alternative. Redirect it to a file and apply it: + +```sh +yacd init > yacd.yaml +yacd up dev -f yacd.yaml +``` + +See [Defining networks](../developer/networks.md) for the scaffold-and-edit +workflow and the [Environment file reference](environment.md) for field details. + ## up ```text @@ -148,14 +169,14 @@ network that is already absent is reported as success. yacd list [flags] ``` -Lists CardanoNetworks in the active namespace (or across all namespaces with -`-A`), projecting each into `name`, `namespace`, `mode`, `ready`, and published -`endpoints`. Renders an aligned table by default or JSON with `--json`. +Lists CardanoNetworks across all namespaces by default, projecting each into +`name`, `namespace`, `mode`, `ready`, and published `endpoints`. Scope to a +single namespace with the global `-n`/`--namespace` flag. Renders an aligned +table by default or JSON with `--json`. -| Flag | Short | Type | Default | Meaning | -| --- | --- | --- | --- | --- | -| `--all-namespaces` | `-A` | bool | `false` | List CardanoNetworks across all namespaces. | -| `--json` | | bool | `false` | Print machine-readable JSON. | +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--json` | bool | `false` | Print machine-readable JSON. | The table columns are `NAME`, `NAMESPACE`, `MODE`, `READY`, `ENDPOINTS`. `READY` reflects a fresh `Ready` condition observed as `True` (a stale status is @@ -204,18 +225,20 @@ empty objects. ## topup ```text -yacd topup NAME [flags] +yacd topup NAME LOVELACE [flags] ``` -Submits a faucet top-up. The flow resolves the target faucet URL (preferring the -cluster-published endpoint unless `--faucet-url` overrides it), gates token -transmission through the trust checks below, fetches the auth token from the -published Secret, then `POST`s to the faucet's `/v1/topups` endpoint. +Submits a faucet top-up. `LOVELACE` is a positional argument: the exact amount to +send, which must be greater than 0. By default `topup` **self-forwards** — it +opens a short-lived port-forward to the cluster faucet (and to Kupo when +`--await` is set), so it works directly from your host with no `yacd run` +wrapper. It gates token transmission through the trust checks below, fetches the +auth token from the published Secret, then `POST`s to the faucet's `/v1/topups` +endpoint. | Flag | Type | Default | Meaning | | --- | --- | --- | --- | | `--address` | string | `""` | Destination Cardano testnet address. Required. | -| `--lovelace` | int | `0` | Exact lovelace amount to send. Required; must be greater than 0. | | `--source` | string | `""` | Faucet source name, for example `utxo1`. Empty lets the faucet pick a default. | | `--faucet-url` | string | `""` | Override the faucet URL from CardanoNetwork status. | | `--trust-faucet-url` | bool | `false` | Allow sending the faucet auth token to a custom non-loopback URL. | @@ -229,18 +252,19 @@ The default target requires the CardanoNetwork to be faucet-ready: a fresh status with `Ready` and `FaucetReady` conditions `True`, a published faucet endpoint, and a published faucet auth Secret. -The cluster-published faucet URL is a cluster-internal Service address -(`http://-faucet..svc.cluster.local:`). It resolves -from an in-cluster caller, but not from your host: run `topup` under -[`yacd run`](#run), which forwards the faucet and exposes it as -`$YACD_FAUCET_URL`, then pass `--faucet-url "$YACD_FAUCET_URL"`. +Faucet transport: with no override, `topup` self-forwards the cluster-internal +faucet Service to a loopback port for the duration of the request. Inside +[`yacd run`](#run) it instead reuses the ambient `YACD_FAUCET_URL` (and +`YACD_KUPO_URL` for `--await`) rather than opening a second forward. An explicit +`--faucet-url` suppresses self-forwarding and targets that URL directly; with an +override, `--await` needs an explicit `--kupo-url` (or `YACD_KUPO_URL`). !!! warning "The faucet token leaves the cluster only with explicit acks" - By default, the token is sent only to the cluster-published faucet URL or a - loopback target. `--faucet-url` pointing at a different non-loopback host - requires `--trust-faucet-url`; a trusted `http://` (plaintext) host - additionally requires `--allow-insecure-faucet-url`. These gates exist to - prevent token exfiltration and plaintext eavesdropping. + By default the token is sent only to a loopback target (the self-forwarded or + `run`-inherited faucet URL). An explicit `--faucet-url` at a non-loopback + host requires `--trust-faucet-url`; a trusted `http://` (plaintext) host + additionally requires `--allow-insecure-faucet-url`. These gates prevent + token exfiltration and plaintext eavesdropping. The `--json` output mirrors the faucet's success envelope: diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index eeb67972..520efdc7 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -225,8 +225,9 @@ spec: ``` To submit a top-up against a running local faucet, use -[`yacd topup`](reference/cli.md). Kupo is required for the `--await` flag, which -waits for the funding transaction to confirm on-chain. +[`yacd topup NAME LOVELACE --address ADDR`](reference/cli.md); it self-forwards +the faucet, so no `yacd run` wrapper is needed. Add `--await` to wait for +on-chain confirmation (this needs Kupo, which is enabled by default). ## Image pull issues From b6bb381146f0fc4aaa7846c4c4dc0bb61f9aa2d5 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 3 Jun 2026 19:49:54 -0700 Subject: [PATCH 6/9] docs: document `yacd install` (session 058) alongside Helm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session 058 added `yacd install`, a CLI-native operator install that renders the bundled chart in-memory and SSA-applies it (no Helm/OCI/Docker). Per the co-equal decision, operator/installation.md now presents `yacd install` and `helm install` as tabbed equals across Install / Upgrading / image-verification / Uninstall, with shared Prerequisites and Verify. The CLI reference gains an `install` section + Commands-table row (namespace default yacd-system, --wait/ --timeout/--dry-run/-f/--set/--set-string, operational-values-only + digest-pin nuance, the Plan: dry-run line). Light cross-links from configuration.md, networks.md (the verbs need an installed operator), and the Home routing. Verified: mkdocs build --strict clean; `yacd install --help` matches; live smoke on a fresh k3d cluster — dry-run (install, installed none -> v0.1.1) -> real install (yacd-system created, manager Ready, CRDs registered) -> re-apply dry-run -> `yacd up demo` reconciled to Ready -> clean teardown. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/developer/networks.md | 5 ++ docs/index.md | 5 +- docs/operator/installation.md | 122 +++++++++++++++++++++----------- docs/reference/cli.md | 48 +++++++++++++ docs/reference/configuration.md | 12 +++- 5 files changed, 147 insertions(+), 45 deletions(-) diff --git a/docs/developer/networks.md b/docs/developer/networks.md index 62e72554..be7a0fc3 100644 --- a/docs/developer/networks.md +++ b/docs/developer/networks.md @@ -6,6 +6,11 @@ it down. Each `yacd` command keys off a `NAME` argument; the `NAME` becomes the `CardanoNetwork` name and, unless you pass `--namespace`, the namespace as well, so each environment lands in its own isolated namespace by default. +!!! note "These commands need the operator" + `yacd up` and the verbs below assume the YACD operator is installed in the + target cluster. `yacd devnet` installs it into a local cluster for you; for + any other cluster, run [`yacd install`](../operator/installation.md) first. + For what each Environment field means, see the [Environment file reference](../reference/environment.md) and the [CardanoNetwork reference](../reference/cardanonetwork.md). For complete, diff --git a/docs/index.md b/docs/index.md index 8a109567..18b7a380 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,8 +24,9 @@ yacd ships as two surfaces: === "Running on a cluster" - Install the operator into an existing Kubernetes cluster so a team can share - YACD-managed networks. See [Operator installation](operator/installation.md). + Install the operator into an existing Kubernetes cluster — one `yacd install`, + or Helm — so a team can share YACD-managed networks. See + [Operator installation](operator/installation.md). ## Try it in one command diff --git a/docs/operator/installation.md b/docs/operator/installation.md index da591087..8961ef94 100644 --- a/docs/operator/installation.md +++ b/docs/operator/installation.md @@ -1,35 +1,48 @@ # Installation -Install the YACD operator on a remote Kubernetes cluster with [Helm](https://helm.sh) from the OCI chart published to GitHub Container Registry. The chart deploys the controller manager, its RBAC and ServiceAccount, a secured metrics Service, and the `CardanoNetwork` and `CardanoDBSync` CRDs. +The YACD operator can be installed two equivalent ways: with the `yacd` CLI, which renders and applies the bundled chart directly (no Helm required), or with [Helm](https://helm.sh) from the OCI chart published to GitHub Container Registry. Either way deploys the controller manager, its RBAC and ServiceAccount, a secured metrics Service, and the `CardanoNetwork` and `CardanoDBSync` CRDs. -This page covers a remote install. For a local [k3d](https://k3d.io) cluster, the `yacd` CLI manages its own cluster lifecycle; see the [CLI reference](../reference/cli.md). For every chart value and manager flag, see the [configuration reference](../reference/configuration.md). +This page covers installing onto an existing cluster. For a local [k3d](https://k3d.io) cluster, the `yacd` CLI manages its own cluster lifecycle — `yacd devnet` installs the operator for you; see the [CLI reference](../reference/cli.md). For every chart value and manager flag, see the [configuration reference](../reference/configuration.md). ## Prerequisites - A Kubernetes cluster, version 1.29 or later (the chart sets `kubeVersion: ">= 1.29.0-0"`). -- Helm 3.8 or later, which can pull OCI charts. - `kubectl` configured against the target cluster. +- For `yacd install`: the [`yacd` CLI](../reference/cli.md#install) on your `PATH` (no Helm needed). +- For the Helm path: Helm 3.8 or later, which can pull OCI charts. ## Install -Install the chart into a namespace, creating the namespace if it does not exist. Replace `` with the release version you want (for example `0.1.1`) and `` with your target namespace. +=== "yacd install" -```sh -helm install yacd oci://ghcr.io/meigma/yacd/chart \ - --version \ - --namespace \ - --create-namespace -``` + `yacd install` renders the operator's bundled chart in memory and applies it to the cluster your kubeconfig points at. It installs the operator when absent and upgrades it otherwise, then waits for the manager to become ready. -The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.1.1` ships `appVersion` `v0.1.1`). To discover the chart metadata before installing, run: + ```sh + yacd install --namespace + ``` -```sh -helm show chart oci://ghcr.io/meigma/yacd/chart --version -``` + The namespace defaults to `yacd-system` and is created if it does not exist. `--wait` (default true) blocks until the manager Deployment is Available, up to `--timeout` (default `5m`). Preview the action without changing the cluster with `--dry-run`. The operator version is the one this CLI embeds; to move it, upgrade the CLI. Pass operational value overrides with `-f`/`--set`/`--set-string` — see the [CLI reference](../reference/cli.md#install). + +=== "Helm" -The chart bundles the CRDs under `charts/yacd/crds/`, so Helm installs `cardanonetworks.yacd.meigma.io` and `cardanodbsyncs.yacd.meigma.io` as part of the release. You do not apply CRDs separately. + Install the chart into a namespace, creating it if it does not exist. Replace `` with the release version you want (for example `0.1.1`) and `` with your target namespace. -To override any value, pass `--set` or `-f values.yaml`. The full value surface (image, metrics, RBAC, leader election, manager logging, Kyverno, security context, scheduling) lives in the [configuration reference](../reference/configuration.md). + ```sh + helm install yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace \ + --create-namespace + ``` + + The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.1.1` ships `appVersion` `v0.1.1`). To inspect the chart metadata before installing: + + ```sh + helm show chart oci://ghcr.io/meigma/yacd/chart --version + ``` + + Override values with `--set` or `-f values.yaml`. + +Both methods bundle the CRDs `cardanonetworks.yacd.meigma.io` and `cardanodbsyncs.yacd.meigma.io`, so you do not apply CRDs separately. The full value surface (image, metrics, RBAC, leader election, manager logging, Kyverno, security context, scheduling) lives in the [configuration reference](../reference/configuration.md). ## Verify @@ -49,16 +62,26 @@ Operators drive networks with the same `yacd` verbs documented in the [CLI refer ## Upgrading -Upgrade to a new chart version in place: +=== "yacd install" -```sh -helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ - --version \ - --namespace -``` + Re-run `yacd install` against the cluster. It upgrades an older same-major install and re-applies an equal version to heal drift; it refuses a newer or major-mismatched in-cluster version with actionable guidance. Because the operator version is pinned to the CLI, **upgrade the CLI to move the operator version**, then re-run: + + ```sh + yacd install --namespace + ``` + +=== "Helm" + + Upgrade to a new chart version in place: + + ```sh + helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace + ``` -!!! warning "Helm does not upgrade bundled CRDs" - The CRDs ship under the chart's `crds/` directory. Helm installs those CRDs on first install but **never upgrades or deletes them** on `helm upgrade` or `helm uninstall`. When a release changes a CRD schema, apply the updated definitions yourself before or after the upgrade: +!!! warning "Helm does not upgrade bundled CRDs (Helm path only)" + `yacd install` re-renders the CRDs from the embedded chart, so they travel with the CLI version. Helm, by contrast, installs the CRDs under the chart's `crds/` directory on first install but **never upgrades or deletes them** on `helm upgrade` or `helm uninstall`. When a release changes a CRD schema, apply the updated definitions yourself before or after the Helm upgrade: ```sh kubectl apply -f https://raw.githubusercontent.com/meigma/yacd/v/charts/yacd/crds/yacd.meigma.io_cardanonetworks.yaml @@ -79,34 +102,51 @@ The release workflow attests the manager image, faucet image, and Helm chart wit Enabling it requires a running Kyverno installation in the cluster. When enabled with no explicit `imageReferences`, the policy verifies the configured manager and faucet image repositories, requiring a keyless Sigstore attestation from the `meigma/yacd` release workflow. To enable it: -```sh -helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ - --version \ - --namespace \ - --set kyverno.imageVerification.enabled=true -``` +=== "yacd install" + + ```sh + yacd install --namespace \ + --set kyverno.imageVerification.enabled=true + ``` + +=== "Helm" + + ```sh + helm upgrade yacd oci://ghcr.io/meigma/yacd/chart \ + --version \ + --namespace \ + --set kyverno.imageVerification.enabled=true + ``` The attestor issuer, subject pattern, Rekor URL, validation failure action, and image references are all configurable; see the `kyverno.imageVerification.*` values in the [configuration reference](../reference/configuration.md). To verify a pulled chart or image directly with the GitHub CLI instead of at admission time, use `gh attestation verify`. ## Uninstall -Remove the release: +!!! note "`yacd uninstall` is not yet available" + Removing the operator is a manual step today; the path depends on how you installed it. -```sh -helm uninstall yacd --namespace -``` +=== "yacd install" + + Delete the install namespace (which removes the manager Deployment, ServiceAccount, Services, and namespaced RBAC) and the chart's cluster-scoped RBAC: + + ```sh + kubectl delete namespace + kubectl delete clusterrole,clusterrolebinding -l app.kubernetes.io/name=yacd + ``` + +=== "Helm" + + Remove the release: + + ```sh + helm uninstall yacd --namespace + ``` !!! warning "CRDs and custom resources are not removed" - `helm uninstall` does not delete the CRDs installed from `crds/`, and it does not delete any `CardanoNetwork` or `CardanoDBSync` resources you created. Delete those resources first if you want the operator to tear down their owned runtime children, then remove the CRDs manually once nothing depends on them: + Neither path deletes the CRDs or any `CardanoNetwork` or `CardanoDBSync` resources you created. Delete those resources first if you want the operator to tear down their owned runtime children, then remove the CRDs once nothing depends on them: ```sh kubectl delete cardanonetworks --all --all-namespaces kubectl delete cardanodbsyncs --all --all-namespaces kubectl delete crd cardanonetworks.yacd.meigma.io cardanodbsyncs.yacd.meigma.io ``` - -To remove the namespace if you created it solely for YACD: - -```sh -kubectl delete namespace -``` diff --git a/docs/reference/cli.md b/docs/reference/cli.md index f0f5b78f..dc354e10 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -23,6 +23,7 @@ Commands: | `devnet` | Bring up a local Cardano devnet (cluster, operator, and a funded network). | | `devnet down` | Delete the managed devnet cluster. | | `devnet status` | Show the managed devnet cluster, operator, and network status. | +| `install` | Install or upgrade the YACD operator on a cluster. | | `init` | Print a commented `yacd.yaml` environment template to stdout. | | `up NAME` | Create or update a YACD environment and wait for readiness. | | `down NAME` | Delete a YACD environment and wait for clean removal. | @@ -103,6 +104,53 @@ Read-only unified view of the managed cluster, operator, and networks. Takes no flags beyond the [global flags](#global-flags). Prints a one-line hint when no managed cluster exists. +## install + +```text +yacd install [flags] +``` + +Installs or upgrades the YACD operator on the targeted cluster, then waits for +the manager to become ready. The target is the explicit `--kubeconfig`/`--context` +(or the `YACD_KUBECONFIG`/`YACD_KUBE_CONTEXT` variables), otherwise the ambient +current-context; `install` never targets the managed devnet. The global +`-n`/`--namespace` flag selects the install namespace and defaults to +`yacd-system` (created if absent). + +`install` reconciles the cluster to the operator version this CLI embeds: it +installs when absent, upgrades an older same-major install, re-applies an equal +version to heal drift, and refuses a newer or major-mismatched in-cluster version +with actionable guidance. The operator image is digest-pinned to the embedded +version; the supported way to change the operator version is to upgrade the CLI. + +| Flag | Short | Type | Default | Meaning | +| --- | --- | --- | --- | --- | +| `--wait` | | bool | `true` | Wait for the manager Deployment to become Available. | +| `--timeout` | | duration | `5m0s` | Maximum time to wait for readiness (also bounds a `--dry-run` plan's reads). Must be greater than 0 when `--wait` is set. | +| `--dry-run` | | bool | `false` | Report the planned action without changing the cluster. | +| `--values` | `-f` | stringArray | `[]` | Path to a YAML file of operational chart value overrides (repeatable; later files win). | +| `--set` | | stringArray | `[]` | Set an operational chart value (Helm `--set` syntax, repeatable). | +| `--set-string` | | stringArray | `[]` | Set an operational chart value forced to a string (repeatable). | + +The override flags customize **operational** chart values (replicas, resources, +scheduling, logging, metrics, and so on), validated against the chart's schema so +a bad value fails fast (under `--dry-run` too). Precedence, later wins: `-f` files +(in order) < `--set` < `--set-string`. Because user values deep-merge over the +defaults, `--set image.tag` is ineffective (the chart renders `repository@digest` +and the pinned digest wins); `--set image.digest` or `image.repository` do +repoint the operator image but are not a supported configuration. + +`--dry-run` prints the action the next install would take and changes nothing: + +```text +Plan: install operator (installed none -> v0.1.1) in namespace yacd-system +``` + +See [Installation](../operator/installation.md) for the full operator-install +workflow (including the Helm alternative), and the +[configuration reference](configuration.md) for every value the override flags +accept. + ## init ```text diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 58158a09..909c690b 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -3,12 +3,20 @@ Reference for configuring the YACD operator. There are two layers: 1. **Helm chart values** — what you set in `values.yaml` (or `--set`) when you - install the chart. These render the operator Deployment, RBAC, metrics - Service, and the optional Kyverno image-verification policy. + install the operator. Both `helm install` and `yacd install` consume these + values; `yacd install` takes them via `-f`/`--set`/`--set-string` and + validates them against the chart schema. They render the operator Deployment, + RBAC, metrics Service, and the optional Kyverno image-verification policy. 2. **Manager flags** — the command-line flags the manager process accepts. The chart translates the relevant values into these flags automatically; set them directly only when running the manager binary outside the chart. +!!! note "Operator image with `yacd install`" + With `yacd install`, the operator image is digest-pinned to the CLI build, so + `image.tag` is inert and the supported way to change the operator version is + to upgrade the CLI. The other operational values behave the same across both + install methods. + This page documents the operator's own configuration. For the `yacd` CLI flags, environment variables, and `endpoints.json` schema, see [CLI reference](cli.md). For the per-network and per-db-sync resource fields, see From 105870c2310c0914371c71887b3421f89b65ce4d Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Thu, 4 Jun 2026 19:30:04 -0700 Subject: [PATCH 7/9] docs: faucet removal + CLI-native wallets (sessions 059+061), appVersion install pin, 0.2.0 Sessions 059 and 061 removed the in-cluster HTTP faucet entirely: `yacd topup` is gone, `spec.chainAPI.{faucet,wallet}` and the FaucetReady/WalletReady conditions are deleted, and the YACD_FAUCET_* env vars and endpoints.json faucetUrl are removed. Funding is now CLI-native: every local network gets a genesis-funded `faucet` wallet, and `yacd wallet {list,add,topup,export,remove}` builds, signs, and submits transactions directly over Ogmios/Kupo. - Rewrote funding.md around `yacd wallet`; updated getting-started step 5, connecting-tools, testing, troubleshooting, recipes, index, and the architecture/security concepts to the wallet model (security now covers wallet key custody + local signing instead of the deleted topup trust gate). - reference/cli.md: replaced the `topup` section with a `wallet` section, dropped the faucet rows from the YACD_* contract / endpoints.json / list / info, retabled the Commands list. - reference/cardanonetwork.md / environment.md: removed the faucet/wallet spec, status, conditions, and explicit-field rows. - Install (#109/#87): operator is now appVersion-tag-pinned (not digest); fixed the install docs (`image.*` all repoint now), bumped version references to 0.2.0/v0.2.0, and dropped the removed faucet image/flag from configuration.md. Verified: mkdocs build --strict clean; full residual-reference sweep clean; `yacd wallet`/`install` --help match. Live smoke pending. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/concepts/architecture.md | 21 ++-- docs/concepts/security.md | 153 +++++++------------------ docs/developer/connecting-tools.md | 33 +++--- docs/developer/funding.md | 136 +++++++--------------- docs/developer/getting-started.md | 38 +++---- docs/index.md | 4 +- docs/operator/installation.md | 8 +- docs/operator/testing.md | 5 +- docs/recipes.md | 27 ++--- docs/reference/cardanonetwork.md | 63 ++-------- docs/reference/cli.md | 177 ++++++++++++++++------------- docs/reference/configuration.md | 18 ++- docs/reference/environment.md | 1 - docs/troubleshooting.md | 63 ++++------ 14 files changed, 279 insertions(+), 468 deletions(-) diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index 58ff9d04..f2593202 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -52,8 +52,8 @@ the services exposed next to the node. The controller reconciles the node into a workload backed by a PVC for the node database, publishes resolved network identity and cluster-local Service endpoints into status, and tracks health through a set of `metav1.Condition` entries (`Ready`, `NodeReady`, -`NodeSynchronized`, `OgmiosReady`, `KupoReady`, `FaucetReady`, `WalletReady`, -`ArtifactsReady`, and others). Status reports only what the controller can +`NodeSynchronized`, `OgmiosReady`, `KupoReady`, `ArtifactsReady`, and others). +Status reports only what the controller can observe in-cluster, so consumers do not trust stale or hand-edited values. Two chain-access services are enabled by default, because a raw `cardano-node` @@ -61,9 +61,10 @@ is not enough to build against. [Ogmios](https://ogmios.dev) is the default chain-access API: it gives YACD and developers a JSON/RPC interface for query, submit, and evaluate without every client having to share the node's Unix socket. [Kupo](https://cardanosolutions.github.io/kupo/) is the default chain -index. A local-only faucet is also available but is **opt-in**, because it -exposes a spending endpoint; it must be explicitly enabled. For the exact -defaults, ports, and images, see the +index. Funding is CLI-native: every local network is created with a +genesis-funded `faucet` wallet, and the `yacd wallet` verbs build and submit +funding transactions directly over Ogmios and Kupo — there is no in-cluster +faucet service. For the exact defaults, ports, and images, see the [CardanoNetwork reference](../reference/cardanonetwork.md). ## Supporting-service CRDs: CardanoDBSync @@ -174,11 +175,11 @@ table, and the `endpoints.json` schema, see the [CLI reference](../reference/cli.md) and the [connecting tools guide](../developer/connecting-tools.md). -!!! warning "The faucet is local-only and host-gated" - The faucet funds addresses on local development networks only. The CLI - forwards it to loopback and exempts the loopback URL from its trust gate; - targeting a custom non-loopback faucet URL from the host requires explicit - trust flags so the bearer token is never sent to an unexpected host. See the +!!! note "Funding is CLI-native" + Local networks are created with a genesis-funded `faucet` wallet. The `yacd + wallet` verbs spend from it by building and signing transactions on the host + and submitting them over Ogmios and Kupo; wallet keys live in labeled + Kubernetes Secrets and never leave for a server-side signer. See the [funding guide](../developer/funding.md) and the [security model](security.md). diff --git a/docs/concepts/security.md b/docs/concepts/security.md index 1d3c7d8b..df0a470e 100644 --- a/docs/concepts/security.md +++ b/docs/concepts/security.md @@ -1,110 +1,43 @@ # Security model -YACD is a development environment manager, so its threat model is shaped by two -facts: the faucet is a live spending endpoint, and the operator runs with -cluster-wide reach. This page explains the deliberate security decisions that -follow from those facts: why the faucet auth token never leaves the host, why -`yacd topup` gates custom faucet URLs, what the cluster-scoped manager RBAC +YACD is a development environment manager, and two facts shape its threat model: +wallet keys are real signing credentials, and the operator runs with cluster-wide +reach. This page explains the deliberate security decisions that follow: how +wallet keys are stored and signed with, what the cluster-scoped manager RBAC means on a shared cluster, and how the chart's optional image-verification path -hardens the supply chain. For the concrete knobs, follow the links to the -how-to and reference pages. - -## The faucet auth token is host-only - -A network with the faucet enabled publishes a Kubernetes Secret holding a Bearer -token. Any request that spends from the faucet must carry that token, so the -token is a credential worth protecting. - -YACD treats the token as **host-only**: it is read on the developer's machine, -used to authenticate faucet calls over a port-forward, and exposed to host -tooling through the `YACD_FAUCET_TOKEN` environment variable. It is **never** -injected into a process running inside the cluster. - -That asymmetry is intentional. The in-pod execution path (`yacd exec`) builds -its `YACD_*` environment without the token. From the source comment that -documents the omission: - -> It intentionally omits `YACD_FAUCET_TOKEN` — a Bearer token injected into the -> exec argv would land in apiserver audit logs and /proc, and in-pod tooling -> does not need it. - -Two leak channels drive the decision: - -- **apiserver audit logs.** Launching an in-pod process means handing an `exec` - request to the Kubernetes API server. If the token rode in the argv or - environment of that request, it would be captured verbatim in the cluster's - audit log, where it persists and is visible to anyone with audit access. -- **`/proc`.** A process's argv and environment are readable through `/proc` by - other processes in the same pod or by anything with host visibility, so a - token placed there is exposed for the lifetime of the process. - -In-pod tooling has no need for the token in the first place: it talks to the -cluster-internal Ogmios, Kupo, and node socket directly. Keeping the token on -the host removes the credential from both leak channels without losing any -capability. - -!!! warning "The faucet is local-only" - The faucet exposes a spending endpoint and is intended for local - development networks only. The host-only token rule is part of why: it keeps - a spending credential off the cluster's API surface. - -## The `yacd topup` trust gate - -`yacd topup` sends the faucet auth token to a faucet URL. By default that URL is -the one the `CardanoNetwork` published in its status, reached over a loopback -port-forward. The `--faucet-url` flag lets you override that destination, which -reintroduces the risk of sending a live credential somewhere it should not go. - -A trust gate guards the override. It only engages when you actually point -`--faucet-url` at something that differs from the published URL and is not a -loopback target. When the URL matches what the cluster published, or resolves to -`localhost` or a loopback IP, the gate stays out of your way. Outside those -cases it defends three distinct attack vectors, each with its own -acknowledgement flag. - -### Vector 1: token exfiltration to an attacker-supplied host - -`--faucet-url` accepts any HTTP endpoint. If you (or a script you ran) pointed it -at an attacker-controlled host, the request would carry the Bearer token -straight to that host. The gate's first job is to notice that the destination is -neither the cluster-published URL nor a loopback target, and to refuse rather -than leak the token to an unknown host. - -### Vector 2: accidental exposure via a non-loopback override - -Even a legitimate remote operator may not realise that a custom `--faucet-url` -leaves the cluster boundary. A custom non-loopback destination is therefore -refused by default; sending the token there requires the explicit -acknowledgement `--trust-faucet-url`. The error names the Secret and the -destination host so you can confirm you meant it: - -```text -refusing to send faucet auth Secret / token to custom faucet URL host ""; pass --trust-faucet-url to allow this destination -``` - -### Vector 3: plaintext eavesdropping - -A trusted custom URL using `http://` would transmit the Bearer token in -cleartext, where any on-path observer can read it. The gate is HTTPS-only by -default even for a trusted destination, so an `http://` custom URL needs a second -acknowledgement, `--allow-insecure-faucet-url`, on top of `--trust-faucet-url`: - -```text -refusing to send faucet auth Secret / token to insecure custom faucet URL host ""; pass --allow-insecure-faucet-url with --trust-faucet-url to allow HTTP -``` - -The two flags compound: trust authorises the destination, and the insecure flag -separately authorises plaintext to it. Neither one implies the other, so you -cannot send a token in cleartext to a remote host by accident. For the flag -defaults and the full `topup` surface, see the [CLI reference](../reference/cli.md); -for the funding workflow, see the [funding how-to](../developer/funding.md). +hardens the supply chain. For the concrete knobs, follow the links to the how-to +and reference pages. + +## Wallet key custody + +Funding a wallet means signing a real transaction, so wallet keys are credentials +worth protecting. YACD keeps custody simple and Kubernetes-native. + +- **Keys live in labeled Kubernetes Secrets.** Each managed wallet is a + `-wallet-` Secret in the network's namespace, holding the payment + signing key, verification key, and address. The genesis-funded `faucet` wallet + has the same shape (`-wallet-faucet`) and is created and owned by the + operator. Storing keys as Secrets means they inherit Kubernetes RBAC, + encryption-at-rest, and backup rather than living in a bespoke store. +- **The CLI signs locally; the cluster never signs for you.** `yacd wallet` reads + the source wallet's signing key, builds and signs the funding transaction on + your machine, and submits it over Ogmios. There is no server-side signing + endpoint and no faucet HTTP service to authenticate against, so no long-lived + spending credential is exposed inside the cluster. +- **`export` writes keys deliberately.** `yacd wallet export` is the only path + that puts raw key material on local disk; it writes `0600` files under a `0700` + directory and never prints keys to stdout. + +Because there is no in-cluster faucet service, there is no Bearer token to +distribute and no token-to-URL trust gate to reason about. Keys stay in Secrets, +and signing stays on the host. ## Cluster-scoped manager RBAC -The chart binds the manager's ServiceAccount to a `ClusterRole`, not a -namespaced `Role`. The grant is broad by design: the operator manages -`CardanoNetwork` and `CardanoDBSync` objects and reconciles them into the core -Kubernetes workloads they need. +The chart binds the manager's ServiceAccount to a `ClusterRole`, not a namespaced +`Role`. The grant is broad by design: the operator manages `CardanoNetwork` and +`CardanoDBSync` objects and reconciles them into the core Kubernetes workloads +they need. The `ClusterRole` grants, across all namespaces: @@ -120,10 +53,10 @@ The `ClusterRole` grants, across all namespaces: The implication for a **shared cluster** is direct: a YACD install can create, read, and delete Secrets, Services, ConfigMaps, PVCs, and Deployments in any namespace, because that is the scope the reconcilers operate at. The `secrets` -verbs include `create` and `delete` because the faucet auth Secret is a managed -object. Treat the operator as a cluster-wide actor when you decide where to -install it; an isolated development cluster is the intended home, not a -multi-tenant production cluster shared with untrusted workloads. +verbs include `create` and `delete` because wallet keys and other runtime +material are managed Secrets. Treat the operator as a cluster-wide actor when you +decide where to install it; an isolated development cluster is the intended home, +not a multi-tenant production cluster shared with untrusted workloads. RBAC creation is on by default but optional (`rbac.create`), and the role and binding names are templated so an operator can substitute externally managed @@ -132,11 +65,11 @@ RBAC. See the [installation guide](../operator/installation.md) and the ## Supply-chain image verification -The release workflow attests the manager image, the faucet image, and the Helm -chart with GitHub-native (Sigstore keyless) attestations. The chart ships an -**opt-in** Kyverno `ClusterPolicy` that verifies the manager and faucet image -attestations at Pod admission time, closing the gap between "an image is signed -somewhere" and "only verified images run in this cluster". +The release workflow attests the manager image and the Helm chart with +GitHub-native (Sigstore keyless) attestations. The chart ships an **opt-in** +Kyverno `ClusterPolicy` that verifies the manager image attestation at Pod +admission time, closing the gap between "an image is signed somewhere" and "only +verified images run in this cluster". The policy is **disabled by default** (`kyverno.imageVerification.enabled: false`) because it depends on a running [Kyverno](https://kyverno.io) diff --git a/docs/developer/connecting-tools.md b/docs/developer/connecting-tools.md index 25dfb488..28af94ba 100644 --- a/docs/developer/connecting-tools.md +++ b/docs/developer/connecting-tools.md @@ -88,11 +88,6 @@ stream without a TTY: yacd exec my-net -- sh ``` -!!! warning "The faucet token is host-only" - `YACD_FAUCET_TOKEN` is never set in the `exec` environment. A Bearer token - on the in-pod command line would leak into apiserver audit logs. Tools that - need the token must run on the host under `run` or `connect`. - ## Hold forwards open Use `yacd connect NAME` when several host processes need the endpoints at once, @@ -124,27 +119,27 @@ with the same name in different namespaces do not collide: ``` Read the loopback URLs from that file in any host process. The file is created -`0600` under `0700` directories, its ports are only live while `connect` is -running, and it **never contains the faucet token**. For the document's field -names and shape, see the [endpoints.json schema](../reference/cli.md). +`0600` under `0700` directories, holds no secrets, and its ports are only live +while `connect` is running. For the document's field names and shape, see the +[endpoints.json schema](../reference/cli.md). -## Fund an address from a test +## Fund a wallet from a test -`yacd topup NAME LOVELACE --address ADDR --await` funds `ADDR` through the faucet -and polls Kupo until the funding transaction's output appears, so a test never -races chain inclusion. `topup` self-forwards the faucet and Kupo on its own, so -it needs no `yacd run` wrapper and no URL flags: +`yacd wallet topup NET WALLET LOVELACE --await` funds a target and polls Kupo +until the funding transaction's output appears, so a test never races chain +inclusion. The `WALLET` argument is a managed wallet name or a bech32 +`addr_test...` address, and `topup` forwards Ogmios and Kupo itself — no `yacd +run` wrapper and no URL flags: ```sh export ADDR=addr_test1... # the address your test funds -yacd topup my-net 1000000 --address "$ADDR" --await +yacd wallet topup my-net "$ADDR" 1000000 --await ``` -Run inside `yacd run`, `topup` instead inherits the ambient `YACD_FAUCET_URL` -and `YACD_KUPO_URL` and reuses those forwards rather than opening a second one. -Either way the loopback faucet URL is exempt from the `topup` trust gate, so no -`--trust-faucet-url` is needed. See the [CLI reference](../reference/cli.md) for -all `topup` flags. +Funds come from the network's genesis `faucet` wallet by default; `--from` +selects another managed wallet. See the +[CLI reference](../reference/cli.md#wallet) for all `wallet` flags and the +[funding guide](funding.md) for the full wallet workflow. ## See also diff --git a/docs/developer/funding.md b/docs/developer/funding.md index 7094d4c8..f99a5d67 100644 --- a/docs/developer/funding.md +++ b/docs/developer/funding.md @@ -1,117 +1,67 @@ # Fund an account on a local network -Get test ADA into an address on a local network in three steps: enable the -faucet on the Environment, find the address and faucet details with `yacd info`, -then send lovelace with `yacd topup`. - -!!! warning "The faucet is local-only" - The faucet exposes a spending endpoint, so it is opt-in and intended for - local development networks. The faucet auth token is host-only and is never - placed in the in-pod environment. See - [Security](../concepts/security.md) for the trust model. - -## 1. Enable the faucet - -The faucet is off by default. Turn it on in the network's `chainAPI`. Optionally -enable the pre-funded developer wallet too, so you have a funded address from day -zero (the wallet requires the faucet and Kupo, and is local-mode only): - -```yaml -spec: - network: - chainAPI: - faucet: - enabled: true - wallet: - enabled: true -``` - -Apply the manifest and wait for the network to become `Ready`. The faucet and -wallet defaults (sources, per-request lovelace bounds, wallet funding amount) -are documented in -[the CardanoNetwork reference](../reference/cardanonetwork.md). A complete -copy-paste manifest lives in the [recipes](../recipes.md). +Every local YACD network is created with a genesis-funded `faucet` wallet. You +spend from it to fund developer wallets (or any testnet address) with the `yacd +wallet` verbs, which build, sign, and submit transactions directly against the +network's Ogmios and Kupo endpoints. There is no in-cluster faucet service. -`topup` refuses to run until the network publishes a faucet: it requires the -`Ready` and `FaucetReady` conditions to be `True` and fresh, and it reads the -faucet endpoint and auth Secret straight from status. +!!! note "Local networks only" + The genesis-funded `faucet` wallet is created only for `local` networks; it + is funded at genesis from the localnet's initial UTxOs. Public networks + (preview/preprod/mainnet) have no faucet wallet — fund those from your own + funded keys. -## 2. Find the address and faucet details +## Create and fund a wallet -Print the network's status and connection information: +The quickest path is `yacd wallet add`, which generates a managed wallet and, +with `--topup`, funds it from the `faucet` wallet in one step. Add `--await` to +block until the funding transaction is confirmed on-chain: ```sh -yacd info my-net -``` - -The text output includes an `Endpoints` section with the `faucet` URL, a -`Faucet` section with the auth Secret name, and — when the developer wallet is -enabled — a `Wallet` section with its `addr_test...` address and funded state: - -```text -Wallet: - Address: addr_test1... - Key Secret: my-net-wallet-keys - Funded: true +yacd wallet add my-net --topup 5000000 --await ``` -Use that wallet `Address` as your funding target, or supply any other testnet -address you control. Add `--json` for machine-readable output: +This prints the new wallet's name and `addr_test...` address, the funding +transaction id, and `Confirmed on-chain.` once Kupo sees the output. Omit +`--topup` to create an unfunded wallet, and pass `--name` to choose the name +instead of the generated adjective-noun default. -```sh -yacd info my-net --json -``` +Managed wallet keys are stored as labeled Kubernetes Secrets +(`-wallet-`) in the network's namespace; the CLI reads them to +sign locally. See [Security](../concepts/security.md) for the custody model. -## 3. Send lovelace +## Fund an existing wallet or address -Fund an address with an exact lovelace amount. `topup` reaches the faucet on its -own — with no `--faucet-url` it opens a short-lived port-forward, POSTs, and -tears it down — so it works directly from your host with no `yacd run` wrapper: +`yacd wallet topup` funds an existing target with an exact lovelace amount. The +`WALLET` argument is a managed wallet name, a public key, or a bech32 +`addr_test...` address, so you can also fund an address you do not manage: ```sh -yacd topup my-net 1000000 --address addr_test1... +yacd wallet topup my-net bright-sun 1000000 --await +yacd wallet topup my-net addr_test1... 1000000 ``` -`LOVELACE` is a positional argument, `--address` is required, and the amount must -be greater than zero and within the faucet's configured min/max bounds. `topup` -reads the auth token from the published Secret automatically, and the -self-forwarded loopback URL is exempt from the -[trust gate](../concepts/security.md). On success it prints the transaction ID, -source, lovelace, and destination. Add `--json` for a machine-readable result. - -!!! note "Inside `yacd run`, or with an override" - `topup` honors an ambient `YACD_FAUCET_URL` (set inside `yacd run`), so it - works unchanged there without opening a second forward. An explicit - `--faucet-url` suppresses self-forwarding; a custom non-loopback value then - requires `--trust-faucet-url` (and `--allow-insecure-faucet-url` for - `http://`). - -The full `topup` flag set — including `--source`, `--faucet-url`, the -`--trust-faucet-url` / `--allow-insecure-faucet-url` trust gates, and the -`--await` options — is documented in -[the CLI reference](../reference/cli.md). - -## Confirm on-chain +By default the funds come from the `faucet` wallet; pass `--from ` to +spend from another managed wallet instead. `topup` forwards Ogmios and Kupo +itself, so no `yacd run` wrapper or URL flags are needed. Add `--json` for a +machine-readable result. -By default `topup` returns as soon as the faucet accepts the request. To block -until the funding transaction is actually confirmed on-chain, add `--await`, -which polls [Kupo](https://cardanosolutions.github.io/kupo/) for the new output: +## List, export, and remove wallets ```sh -yacd topup my-net 1000000 --address addr_test1... --await +yacd wallet list my-net +yacd wallet export my-net bright-sun +yacd wallet remove my-net bright-sun ``` -When `topup` self-forwards it reuses that same session's Kupo, so `--await` -needs no extra flags. When the output appears, `topup` prints `Confirmed -on-chain.` and exits. If you override the faucet with `--faucet-url`, supply a -matching `--kupo-url` for `--await`. See -[Connecting tools and tests](connecting-tools.md) for how `run` bridges -cluster-internal endpoints to your host. +`list` shows each managed wallet's name, address, and source (the genesis +`faucet` wallet is included). `export` writes the wallet's `.skey`, `.vkey`, and +`.addr` files to `.yacd///wallets//` (override with +`--out`). The `faucet` wallet is reserved and operator-owned, so the CLI will not +remove it. ## See also -- [CLI reference](../reference/cli.md) — every `topup` flag and default. -- [CardanoNetwork reference](../reference/cardanonetwork.md) — faucet and wallet - spec fields and defaults. -- [Security](../concepts/security.md) — the faucet trust gate and host-only - token rationale. +- [CLI reference](../reference/cli.md#wallet) — every `yacd wallet` flag and default. +- [Security](../concepts/security.md) — wallet key custody and local signing. +- [Connecting tools & tests](connecting-tools.md) — funding a wallet from a test run. diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index 8e95b772..e7c9a80e 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -76,9 +76,10 @@ Try: ``` !!! note "Keep these handy" - The `Wallet` line is a pre-funded developer address, and the network uses - network magic `42`. You will use both in the steps below, and the two - commands under `Try:` are exactly the next two things you will run. + The network uses network magic `42` (you will use it when you query the + chain), and the `Wallet` line shows the network's genesis-funded `faucet` + wallet, which funds the wallets you create. The two commands under `Try:` are + the next two things you will run. For what the cluster, operator, and network are and how they relate, see [Architecture](../concepts/architecture.md). @@ -120,29 +121,28 @@ The output is a JSON object describing the tip (slot, block, epoch, and sync percentage). Run it again after a few seconds and the slot advances, confirming the chain is producing blocks. -## 5. Fund an address +## 5. Fund a wallet -Use the local faucet to send funds to an address. `yacd topup` reaches the -faucet on its own (it opens a short-lived port-forward), so no `yacd run` -wrapper is needed. Replace `
` with a Cardano testnet address you -control (the `Wallet` address from step 2 works), and `` with the -amount to send (1 ADA = 1,000,000 lovelace): +Every local network is created with a genesis-funded `faucet` wallet. Create a +new managed wallet and fund it from the faucet in one command, waiting for +on-chain confirmation: ```sh -yacd topup devnet --address
+yacd wallet add devnet --topup 5000000 --await ``` -`LOVELACE` is a positional argument and `--address` is required. On success the -faucet submits a funding transaction and `topup` prints the transaction ID. Add -`--await` to block until the funds are confirmed on-chain. +This generates a wallet, sends it 5,000,000 lovelace (5 ADA) from the `faucet` +wallet, and prints the new wallet's address and the funding transaction id. +`yacd wallet` builds and submits the transaction directly over Ogmios and Kupo; +there is no separate faucet service. -!!! warning "The faucet is local-only" - The faucet and its auth token are part of your local devnet only. Never - point `yacd topup` at a shared or public network, and never send the faucet - token off your machine. +!!! note "Wallets fund local development" + The `faucet` wallet and the wallets you create exist to fund development on + local networks. Public networks have no faucet wallet; fund those from your + own keys. -For the full funding workflow, including waiting for on-chain confirmation, see -the [funding guide](funding.md). +For the full wallet workflow — funding an existing address, listing, exporting, +and removing wallets — see the [funding guide](funding.md). ## 6. Tear it down diff --git a/docs/index.md b/docs/index.md index 18b7a380..335bd169 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,8 +9,8 @@ yacd ships as two surfaces: - A **Kubernetes operator** that owns declarative cluster state. The `CardanoNetwork` CRD reconciles a Cardano node with [Ogmios](https://ogmios.dev) as the default chain API, [Kupo](https://cardanosolutions.github.io/kupo/) as - the default chain index, and an opt-in token-protected faucet for local - top-ups. + the default chain index. Local networks also get a genesis-funded `faucet` + wallet for funding development. - A companion **`yacd` CLI** that owns local developer workflow: standing up a devnet, applying a checked-in config, waiting for readiness, printing connection details, forwarding endpoints to your host, and funding wallets. diff --git a/docs/operator/installation.md b/docs/operator/installation.md index 8961ef94..255b57c4 100644 --- a/docs/operator/installation.md +++ b/docs/operator/installation.md @@ -25,7 +25,7 @@ This page covers installing onto an existing cluster. For a local [k3d](https:// === "Helm" - Install the chart into a namespace, creating it if it does not exist. Replace `` with the release version you want (for example `0.1.1`) and `` with your target namespace. + Install the chart into a namespace, creating it if it does not exist. Replace `` with the release version you want (for example `0.2.0`) and `` with your target namespace. ```sh helm install yacd oci://ghcr.io/meigma/yacd/chart \ @@ -34,7 +34,7 @@ This page covers installing onto an existing cluster. For a local [k3d](https:// --create-namespace ``` - The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.1.1` ships `appVersion` `v0.1.1`). To inspect the chart metadata before installing: + The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.2.0` ships `appVersion` `v0.2.0`). To inspect the chart metadata before installing: ```sh helm show chart oci://ghcr.io/meigma/yacd/chart --version @@ -98,9 +98,9 @@ The Service is a `ClusterIP` named `yacd-controller-manager-metrics-service`, ex ## Supply-chain image verification (optional) -The release workflow attests the manager image, faucet image, and Helm chart with GitHub-native (Sigstore keyless) attestations. The chart includes an opt-in [Kyverno](https://kyverno.io) `ClusterPolicy` that verifies those attestations on admission. It is disabled by default (`kyverno.imageVerification.enabled: false`). +The release workflow attests the manager image and Helm chart with GitHub-native (Sigstore keyless) attestations. The chart includes an opt-in [Kyverno](https://kyverno.io) `ClusterPolicy` that verifies those attestations on admission. It is disabled by default (`kyverno.imageVerification.enabled: false`). -Enabling it requires a running Kyverno installation in the cluster. When enabled with no explicit `imageReferences`, the policy verifies the configured manager and faucet image repositories, requiring a keyless Sigstore attestation from the `meigma/yacd` release workflow. To enable it: +Enabling it requires a running Kyverno installation in the cluster. When enabled with no explicit `imageReferences`, the policy verifies the configured manager image repository, requiring a keyless Sigstore attestation from the `meigma/yacd` release workflow. To enable it: === "yacd install" diff --git a/docs/operator/testing.md b/docs/operator/testing.md index fa7bd3d3..4c00b1fc 100644 --- a/docs/operator/testing.md +++ b/docs/operator/testing.md @@ -45,9 +45,8 @@ yacd run my-net -- go test ./e2e/... Always put `--` before the test command so its own flags are passed through to it rather than parsed by `yacd`. Inside the command, the runner reads endpoints -from `YACD_OGMIOS_URL`, `YACD_KUPO_URL`, and `YACD_FAUCET_URL` (loopback URLs on -the host) plus `YACD_FAUCET_TOKEN` for the host-only faucet. The variable names -and the loopback/faucet-token details are documented once in +from `YACD_OGMIOS_URL` and `YACD_KUPO_URL` (loopback URLs on the host). The +variable names and loopback details are documented once in [Host access and the `YACD_*` contract](../developer/connecting-tools.md). !!! warning "Use `exec`, not `run`, for `cardano-cli`" diff --git a/docs/recipes.md b/docs/recipes.md index 8b5a77a9..b535fb83 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -27,14 +27,14 @@ see [CLI reference](reference/cli.md). ## Local networks -A local network runs a self-contained devnet with a fast block schedule, an -optional faucet, and an optional pre-funded developer wallet. See the +A local network runs a self-contained devnet with a fast block schedule. Every +local network is created with a genesis-funded `faucet` wallet that you fund +developer wallets from with [`yacd wallet`](developer/funding.md). See the [Environment file reference](reference/environment.md) for every field. ### Single-pool local devnet -Use when you want an isolated, fast local Cardano network with a funded -developer wallet and a faucet for top-ups. +Use when you want an isolated, fast local Cardano network. ```yaml apiVersion: yacd.meigma.io/devconfig/v1alpha1 @@ -47,18 +47,9 @@ spec: port: 3001 storage: size: 2Gi - chainAPI: - faucet: - enabled: true - port: 8080 - defaultSource: utxo1 - minTopUpLovelace: 1000000 - # Raised so the operator can fund the developer wallet (100,000 ADA) - # through the faucet within the per-request maximum. - maxTopUpLovelace: 100000000000 - wallet: - enabled: true - fundingLovelace: 100000000000 + # Ogmios and Kupo are enabled by default. A local network is automatically + # given a genesis-funded `faucet` wallet — the network's funded wallet and + # the default source for `yacd wallet topup`. local: networkMagic: 42 era: conway @@ -70,10 +61,6 @@ spec: count: 1 ``` -!!! warning "The faucet is local-only" - The faucet and the pre-funded developer wallet exist only on local - networks. They are never provisioned for public profiles. - ## Public networks A public network syncs a node against an upstream Cardano network. Select the diff --git a/docs/reference/cardanonetwork.md b/docs/reference/cardanonetwork.md index ccee90a1..07d0491e 100644 --- a/docs/reference/cardanonetwork.md +++ b/docs/reference/cardanonetwork.md @@ -2,9 +2,8 @@ `CardanoNetwork` is the YACD custom resource that declares a single Cardano development network: one primary [cardano-node](https://developers.cardano.org) -workload plus its chain APIs ([Ogmios](https://ogmios.dev), -[Kupo](https://cardanosolutions.github.io/kupo/), a local faucet, and an -optional developer wallet). +workload plus its chain APIs ([Ogmios](https://ogmios.dev) and +[Kupo](https://cardanosolutions.github.io/kupo/)). | | | | --- | --- | @@ -175,15 +174,12 @@ The genesis `profile` values control fee and minimum-UTxO behavior: ### spec.chainAPI `chainAPI` configures APIs exposed next to the primary node. Ogmios and Kupo are -enabled by default. The faucet and wallet are opt-in because they expose a -spending endpoint. +enabled by default. | Field | Type | Required | Description | | --- | --- | --- | --- | | `ogmios` | [OgmiosSpec](#specchainapiogmios) | no | Ogmios sidecar and Service. | | `kupo` | [KupoSpec](#specchainapikupo) | no | Kupo sidecar and Service. | -| `faucet` | [FaucetSpec](#specchainapifaucet) | no | Faucet sidecar and Service. | -| `wallet` | [WalletSpec](#specchainapiwallet) | no | Pre-funded developer wallet for local networks. | #### spec.chainAPI.ogmios @@ -203,33 +199,12 @@ spending endpoint. | `port` | integer (int32) | yes | `1442` | `1`–`65535` | Kupo service port. | | `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Kupo container resources. | -#### spec.chainAPI.faucet - -| Field | Type | Required | Default | Constraints | Description | -| --- | --- | --- | --- | --- | --- | -| `enabled` | boolean | yes | `false` | — | Whether the faucet sidecar is deployed. | -| `image` | string | no | — | — | Overrides the faucet image reference. When omitted, the controller uses its configured default. Overrides must use the same repository as the controller's default faucet image; tag or digest may vary. | -| `port` | integer (int32) | yes | `8080` | `1`–`65535` | Faucet service port. | -| `defaultSource` | string | yes | `utxo1` | — | Generated cardano-testnet UTxO source used when a request does not select one explicitly. | -| `minTopUpLovelace` | integer (int64) | yes | `1000000` | minimum `1` | Minimum exact top-up amount. | -| `maxTopUpLovelace` | integer (int64) | yes | `10000000000` | minimum `1` | Maximum exact top-up amount. | -| `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Faucet container resources. | - -!!! warning "The faucet is local-only and spends funds" - The faucet exposes a spending endpoint and is intended for local - development networks only. Mutating requests require the bearer token - published in `status.faucet.authSecretName`. - -#### spec.chainAPI.wallet - -`wallet` configures a pre-funded developer wallet that the controller -bootstraps for local development networks. It requires the faucet and Kupo to be -enabled and is supported in local mode only. - -| Field | Type | Required | Default | Constraints | Description | -| --- | --- | --- | --- | --- | --- | -| `enabled` | boolean | yes | `false` | — | Whether the controller bootstraps a developer wallet. | -| `fundingLovelace` | integer (int64) | yes | `100000000000` | minimum `1` | Amount the controller funds the wallet with on first bring-up, through the faucet. Must not exceed the faucet's `maxTopUpLovelace`. | +!!! note "No faucet field — funding is CLI-native" + There is no `chainAPI.faucet` or `chainAPI.wallet` field. Every local network + is created with a genesis-funded `faucet` wallet, stored as a + `-wallet-faucet` Secret; fund developer wallets from it with the + [`yacd wallet`](cli.md#wallet) verbs. The faucet wallet has no `spec` knobs + and no `status` block. ## status @@ -241,8 +216,6 @@ the controller and are read-only. | `observedGeneration` | integer (int64) | Most recent generation observed by the controller. | | `network` | [CardanoNetworkIdentityStatus](#statusnetwork) | Resolved network identity once chain material is generated or loaded. | | `endpoints` | [CardanoNetworkEndpointsStatus](#statusendpoints) | Cluster-local connection details for clients and supporting controllers. | -| `faucet` | [FaucetStatus](#statusfaucet) | Faucet-specific runtime details. | -| `wallet` | [WalletStatus](#statuswallet) | Bootstrapped developer wallet details. | | `sync` | [CardanoNetworkSyncStatus](#statussync) | Primary node chain synchronization status inferred from in-cluster sources. | | `conditions` | [][Condition](#conditions) | Current state of the resource (list-map keyed by `type`). | @@ -266,7 +239,6 @@ Each endpoint is a [ServiceEndpointStatus](#serviceendpointstatus). | `nodeToNode` | ServiceEndpointStatus | Primary node-to-node endpoint. | | `ogmios` | ServiceEndpointStatus | Ogmios JSON/RPC endpoint. | | `kupo` | ServiceEndpointStatus | Kupo chain index HTTP endpoint. | -| `faucet` | ServiceEndpointStatus | Local development faucet HTTP endpoint. | | `artifacts` | ServiceEndpointStatus | cardano-tools `serve` HTTP endpoint exposing the staged network artifact files and `manifest.json`. | #### ServiceEndpointStatus @@ -277,21 +249,6 @@ Each endpoint is a [ServiceEndpointStatus](#serviceendpointstatus). | `port` | integer (int32) | Service port. | | `url` | string | Convenience URL for protocols with a stable URL shape. | -### status.faucet - -| Field | Type | Description | -| --- | --- | --- | -| `authSecretName` | string | Same-namespace Secret containing the bearer token used by mutating faucet requests. | - -### status.wallet - -| Field | Type | Description | -| --- | --- | --- | -| `address` | string | Wallet's enterprise testnet payment address (`addr_test...`). | -| `keySecretName` | string | Same-namespace Secret holding the wallet's signing and verification key envelopes. | -| `funded` | boolean | Whether the wallet's funding has been confirmed on-chain. | -| `fundedTxID` | string | Faucet transaction that funded the wallet. | - ### status.sync `sync` reports the primary node's observed synchronization state using only @@ -334,7 +291,5 @@ entries, keyed by a unique `type`. Each condition `status` is one of `True`, | `ArtifactsReady` | The network artifact bundle is staged and served over HTTP. | | `OgmiosReady` | Ogmios is enabled and connected to the primary node. | | `KupoReady` | Kupo is enabled and synchronized enough to serve its API. | -| `FaucetReady` | The faucet is enabled and available through its Service. | -| `WalletReady` | The developer wallet is bootstrapped and funded on-chain. | | `Progressing` | The resource is being created or updated. | | `Degraded` | The resource failed to reach or maintain its desired state. | diff --git a/docs/reference/cli.md b/docs/reference/cli.md index dc354e10..88db57f1 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -29,7 +29,7 @@ Commands: | `down NAME` | Delete a YACD environment and wait for clean removal. | | `list` | List YACD environments across all namespaces (or one with `-n`). | | `info NAME` | Print CardanoNetwork status and connection information. | -| `topup NAME LOVELACE` | Submit a faucet top-up (self-forwards the faucet). | +| `wallet NET` | Manage developer wallets: add, list, topup, export, remove. | | `run NAME [-- command ...]` | Run a command (or a shell) on the host with the `YACD_*` environment wired to forwarded endpoints. | | `connect NAME` | Forward a network's endpoints and hold them open until interrupted. | | `exec NAME -- command ...` | Run a command inside the primary node Pod (for socket-bound tools). | @@ -120,8 +120,9 @@ current-context; `install` never targets the managed devnet. The global `install` reconciles the cluster to the operator version this CLI embeds: it installs when absent, upgrades an older same-major install, re-applies an equal version to heal drift, and refuses a newer or major-mismatched in-cluster version -with actionable guidance. The operator image is digest-pinned to the embedded -version; the supported way to change the operator version is to upgrade the CLI. +with actionable guidance. The operator image is pinned to the chart's appVersion +(the version this CLI embeds); the supported way to change the operator version +is to upgrade the CLI. | Flag | Short | Type | Default | Meaning | | --- | --- | --- | --- | --- | @@ -135,15 +136,15 @@ version; the supported way to change the operator version is to upgrade the CLI. The override flags customize **operational** chart values (replicas, resources, scheduling, logging, metrics, and so on), validated against the chart's schema so a bad value fails fast (under `--dry-run` too). Precedence, later wins: `-f` files -(in order) < `--set` < `--set-string`. Because user values deep-merge over the -defaults, `--set image.tag` is ineffective (the chart renders `repository@digest` -and the pinned digest wins); `--set image.digest` or `image.repository` do -repoint the operator image but are not a supported configuration. +(in order) < `--set` < `--set-string`. The operator `image.*` values are not part +of the supported surface: a `--set image.tag`, `image.repository`, or +`image.digest` will repoint the operator image, but the supported way to change +the operator version is to upgrade the CLI. `--dry-run` prints the action the next install would take and changes nothing: ```text -Plan: install operator (installed none -> v0.1.1) in namespace yacd-system +Plan: install operator (installed none -> v0.2.0) in namespace yacd-system ``` See [Installation](../operator/installation.md) for the full operator-install @@ -159,9 +160,10 @@ yacd init Prints a fully-commented developer environment template to stdout and takes no arguments or flags beyond the [global flags](#global-flags). The active -configuration is a ready-to-run local devnet (faucet plus a pre-funded wallet); -commented blocks document the rest of the API, including chain-API overrides and -a public/mainnet alternative. Redirect it to a file and apply it: +configuration is a ready-to-run local devnet; local networks automatically get a +genesis-funded `faucet` wallet. Commented blocks document the rest of the API, +including chain-API overrides and a public/mainnet alternative. Redirect it to a +file and apply it: ```sh yacd init > yacd.yaml @@ -229,8 +231,8 @@ table by default or JSON with `--json`. The table columns are `NAME`, `NAMESPACE`, `MODE`, `READY`, `ENDPOINTS`. `READY` reflects a fresh `Ready` condition observed as `True` (a stale status is reported as not ready). `ENDPOINTS` is a comma-separated list of published -endpoint names (`node-to-node`, `ogmios`, `kupo`, `faucet`) or `-` when none are -published yet. +endpoint names (`node-to-node`, `ogmios`, `kupo`) or `-` when none are published +yet. The `--json` output is an array of objects with fields: @@ -240,7 +242,7 @@ The `--json` output is an array of objects with fields: | `namespace` | string | CardanoNetwork namespace. | | `mode` | string | Requested network mode (`local` or `public`). | | `ready` | bool | Fresh `Ready` condition observed as `True`. | -| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo`, `faucet` URLs; empty when unpublished. | +| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo` URLs; empty when unpublished. | ## info @@ -265,64 +267,91 @@ empty objects. | `namespace` | string | CardanoNetwork namespace. | | `observedGeneration` | int | Last generation the controller observed. Omitted when 0. | | `network` | object | `mode`, `localnetFingerprint`, `networkMagic`, `profile`, `era`. | -| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo`, `faucet`, each `{serviceName, port, url}` or absent. | -| `faucet` | object | `{authSecretName}`. Omitted when no faucet is published. | -| `wallet` | object | `{address, keySecretName, funded}`. Omitted when no developer wallet exists. | +| `endpoints` | object | `nodeToNode`, `ogmios`, `kupo`, each `{serviceName, port, url}` or absent. | +| `wallet` | object | `{address, keySecretName}` of the genesis-funded `faucet` wallet. Omitted when the network has none (non-local networks). | | `conditions` | array | Each `{type, status, reason, message, observedGeneration, lastTransitionTime}` (RFC3339 timestamp). | -## topup +## wallet ```text -yacd topup NAME LOVELACE [flags] +yacd wallet NET [args] [flags] ``` -Submits a faucet top-up. `LOVELACE` is a positional argument: the exact amount to -send, which must be greater than 0. By default `topup` **self-forwards** — it -opens a short-lived port-forward to the cluster faucet (and to Kupo when -`--await` is set), so it works directly from your host with no `yacd run` -wrapper. It gates token transmission through the trust checks below, fetches the -auth token from the published Secret, then `POST`s to the faucet's `/v1/topups` -endpoint. +Manages developer wallets for a network and funds them by building, signing, and +submitting transactions directly over the network's Ogmios and Kupo endpoints; +there is no in-cluster faucet service. Keys are stored as labeled Kubernetes +Secrets (`-wallet-`) in the network's namespace, and the CLI reads +them to sign locally. + +Every local network has a reserved, operator-owned genesis-funded `faucet` wallet +that funding spends from by default. The `WALLET` argument of `topup`, `remove`, +and `export` accepts a managed wallet name, a public key (hex), or a bech32 +`addr_test...` address. + +### wallet list + +```text +yacd wallet list NET [flags] +``` + +Lists the managed wallets for a network (including `faucet`) with each wallet's +name, address, and source. `--json` prints a machine-readable array. + +### wallet add + +```text +yacd wallet add NET [flags] +``` + +Generates a new managed wallet and, with `--topup`, funds it from the `faucet` +wallet. | Flag | Type | Default | Meaning | | --- | --- | --- | --- | -| `--address` | string | `""` | Destination Cardano testnet address. Required. | -| `--source` | string | `""` | Faucet source name, for example `utxo1`. Empty lets the faucet pick a default. | -| `--faucet-url` | string | `""` | Override the faucet URL from CardanoNetwork status. | -| `--trust-faucet-url` | bool | `false` | Allow sending the faucet auth token to a custom non-loopback URL. | -| `--allow-insecure-faucet-url` | bool | `false` | Allow trusted custom non-loopback HTTP faucet URLs. | +| `--name` | string | generated | Wallet name (default: a generated adjective-noun name). | +| `--topup` | string | `""` | Fund the new wallet with this many lovelace from the faucet. | +| `--await` | bool | `false` | Wait for the funding transaction to confirm on-chain (requires `--topup`). | +| `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. | | `--json` | bool | `false` | Print machine-readable JSON. | -| `--await` | bool | `false` | Wait for the funding transaction to be confirmed on-chain (requires Kupo). | -| `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. Must be greater than 0. | -| `--kupo-url` | string | `""` | Kupo URL for `--await`. Falls back to `YACD_KUPO_URL`. | - -The default target requires the CardanoNetwork to be faucet-ready: a fresh -status with `Ready` and `FaucetReady` conditions `True`, a published faucet -endpoint, and a published faucet auth Secret. - -Faucet transport: with no override, `topup` self-forwards the cluster-internal -faucet Service to a loopback port for the duration of the request. Inside -[`yacd run`](#run) it instead reuses the ambient `YACD_FAUCET_URL` (and -`YACD_KUPO_URL` for `--await`) rather than opening a second forward. An explicit -`--faucet-url` suppresses self-forwarding and targets that URL directly; with an -override, `--await` needs an explicit `--kupo-url` (or `YACD_KUPO_URL`). - -!!! warning "The faucet token leaves the cluster only with explicit acks" - By default the token is sent only to a loopback target (the self-forwarded or - `run`-inherited faucet URL). An explicit `--faucet-url` at a non-loopback - host requires `--trust-faucet-url`; a trusted `http://` (plaintext) host - additionally requires `--allow-insecure-faucet-url`. These gates prevent - token exfiltration and plaintext eavesdropping. - -The `--json` output mirrors the faucet's success envelope: -| Field | Type | Meaning | -| --- | --- | --- | -| `txId` | string | Funding transaction id. | -| `source` | string | Faucet source the funds came from. | -| `sourceAddress` | string | Source address. | -| `destinationAddress` | string | Address that was funded. | -| `lovelace` | int | Lovelace sent. | +### wallet topup + +```text +yacd wallet topup NET WALLET LOVELACE [flags] +``` + +Funds `WALLET` with `LOVELACE` (positional, must be greater than 0) from the +`faucet` wallet, or from another managed wallet with `--from`. It forwards Ogmios +and Kupo itself, so no `yacd run` wrapper or URL flags are needed. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--from` | string | `faucet` | Source wallet name to fund from (default: the faucet wallet). | +| `--await` | bool | `false` | Wait for the funding transaction to confirm on-chain. | +| `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. | +| `--json` | bool | `false` | Print machine-readable JSON. | + +### wallet export + +```text +yacd wallet export NET WALLET [flags] +``` + +Writes the wallet's `.skey`, `.vkey`, and `.addr` files +(mode `0600`) to `.yacd///wallets//`. + +| Flag | Type | Default | Meaning | +| --- | --- | --- | --- | +| `--out` | string | `.yacd///wallets/` | Directory to write the wallet files into. | +| `--force` | bool | `false` | Overwrite existing wallet files. | + +### wallet remove + +```text +yacd wallet remove NET WALLET +``` + +Deletes a managed wallet. The reserved `faucet` wallet cannot be removed. ## run @@ -369,9 +398,9 @@ re-establish), the endpoints file is removed. `connect` has no command-specific flags beyond the [global flags](#global-flags). -!!! note "Loopback ports are ephemeral and token-free" - The endpoints file never contains the faucet token, and its ports are only - live while `connect` is running. See the [endpoints.json schema](#the-endpointsjson-schema). +!!! note "Loopback ports are ephemeral" + The endpoints file holds no secrets, and its ports are only live while + `connect` is running. See the [endpoints.json schema](#the-endpointsjson-schema). ## exec @@ -421,8 +450,6 @@ or removing one is a breaking change to the contract. | `YACD_NETWORK_MAGIC` | network magic (integer) | network magic (integer) | when the controller has published the network magic | | `YACD_OGMIOS_URL` | loopback URL (scheme preserved, e.g. `ws://`) | published ClusterIP URL | when Ogmios is published and forwarded | | `YACD_KUPO_URL` | loopback URL | published ClusterIP URL | when Kupo is published and forwarded | -| `YACD_FAUCET_URL` | loopback URL | published ClusterIP URL | when the faucet is published and forwarded | -| `YACD_FAUCET_TOKEN` | faucet auth Bearer token | *not set* | host `run` only, when the token is non-empty | | `CARDANO_NODE_SOCKET_PATH` | *not set* | `/ipc/node.socket` | in-pod `exec` only | Notes: @@ -436,13 +463,9 @@ Notes: - `CARDANO_NODE_SOCKET_PATH` is unprefixed because that is the name `cardano-cli` already expects. -!!! warning "`YACD_FAUCET_TOKEN` is host-only" - `exec` deliberately omits `YACD_FAUCET_TOKEN`: a Bearer token in the exec - argv would land in apiserver audit logs and `/proc`. In-pod tooling does not - need it. `yacd topup` reads the token from the cluster directly. - -`yacd topup --await` reads `--kupo-url` from `YACD_KUPO_URL` through this -contract, so it works unchanged when run under `yacd run`. +`yacd wallet topup --await` forwards Ogmios and Kupo itself, so it confirms +on-chain without needing these variables; inside `yacd run` it reuses the +forwards already established. ## The endpoints.json schema @@ -457,8 +480,7 @@ contract, so it works unchanged when run under `yacd run`. "namespace": "my-net", "networkMagic": 42, "ogmiosUrl": "ws://127.0.0.1:51820", - "kupoUrl": "http://127.0.0.1:51821", - "faucetUrl": "http://127.0.0.1:51822" + "kupoUrl": "http://127.0.0.1:51821" } ``` @@ -469,10 +491,9 @@ contract, so it works unchanged when run under `yacd run`. | `networkMagic` | int | Network magic. Omitted until the controller publishes it. | | `ogmiosUrl` | string | Loopback Ogmios URL. Omitted when not forwarded. | | `kupoUrl` | string | Loopback Kupo URL. Omitted when not forwarded. | -| `faucetUrl` | string | Loopback faucet URL. Omitted when not forwarded. | -The document deliberately never carries the faucet token, and the ports it lists -are only live while `connect` is running. The file is removed on disconnect. +The ports it lists are only live while `connect` is running, and the file is +removed on disconnect. ## Install diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 909c690b..eaa2e3d6 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -12,10 +12,10 @@ Reference for configuring the YACD operator. There are two layers: directly only when running the manager binary outside the chart. !!! note "Operator image with `yacd install`" - With `yacd install`, the operator image is digest-pinned to the CLI build, so - `image.tag` is inert and the supported way to change the operator version is - to upgrade the CLI. The other operational values behave the same across both - install methods. + With `yacd install`, the operator image is pinned to the chart's `appVersion` + (the version the CLI embeds). Changing `image.*` is not the supported way to + move the operator version — upgrade the CLI instead. The other operational + values behave the same across both install methods. This page documents the operator's own configuration. For the `yacd` CLI flags, environment variables, and `endpoints.json` schema, see @@ -36,10 +36,7 @@ listed. | `image.tag` | `""` | Manager image tag. Empty uses the chart `appVersion`. | | `image.digest` | `""` | Manager image digest (`sha256:...`). When set, it takes precedence over `image.tag`. | | `image.pullPolicy` | `IfNotPresent` | Manager image pull policy. | -| `faucet.image.repository` | `ghcr.io/meigma/yacd/faucet` | Faucet image used for CardanoNetwork faucet sidecars. | -| `faucet.image.tag` | `""` | Faucet image tag. Empty uses the chart `appVersion`. | -| `faucet.image.digest` | `""` | Faucet image digest. When set, it takes precedence over the tag. | -| `cardanoTestnet.image.repository` | `""` | Overrides the cardano-testnet tools image (create-env init container, faucet source-address init container, and the primary cardano-node container when `spec.node.image` is unset). Empty uses the operator's built-in versioned reference. | +| `cardanoTestnet.image.repository` | `""` | Overrides the cardano-testnet tools image (the create-env init container and the primary cardano-node container when `spec.node.image` is unset). Empty uses the operator's built-in versioned reference. | | `cardanoTestnet.image.tag` | `""` | Tag for the cardano-testnet override. Applied only when `repository` is set. | | `cardanoTestnet.image.digest` | `""` | Digest for the cardano-testnet override. When set, it takes precedence over the tag. | | `cardanoTools.image.repository` | `""` | Overrides the cardano-tools utility image used for artifact staging containers. Empty uses the operator's built-in versioned reference. | @@ -153,7 +150,7 @@ requires Kyverno to be installed in the cluster. | `kyverno.imageVerification.name` | `""` | Policy name. Empty derives the name from the release. | | `kyverno.imageVerification.validationFailureAction` | `Enforce` | Policy action when verification fails (`Enforce` or `Audit`). | | `kyverno.imageVerification.webhookTimeoutSeconds` | `30` | Admission webhook timeout for the policy. | -| `kyverno.imageVerification.imageReferences` | `[]` | Image reference globs the policy applies to. Empty falls back to the manager and faucet repositories (each with `:*` and `@*`). | +| `kyverno.imageVerification.imageReferences` | `[]` | Image reference globs the policy applies to. Empty falls back to the manager repository (with `:*` and `@*`). | | `kyverno.imageVerification.attestor.issuer` | `https://token.actions.githubusercontent.com` | Keyless OIDC issuer for the signing identity. | | `kyverno.imageVerification.attestor.subject` | `""` | Exact OIDC subject (certificate identity). | | `kyverno.imageVerification.attestor.subjectRegExp` | `^https://github\.com/meigma/yacd/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$` | OIDC subject regular expression. | @@ -182,6 +179,5 @@ outside the chart. | `--webhook-cert-path` | `""` (unset) | Directory holding the webhook server certificate material. Empty disables the certificate watcher. | | `--webhook-cert-name` | `tls.crt` | Webhook certificate filename within `--webhook-cert-path`. | | `--webhook-cert-key` | `tls.key` | Webhook private key filename within `--webhook-cert-path`. | -| `--default-faucet-image` | `ghcr.io/meigma/yacd/faucet:dev` | Faucet image used when a CardanoNetwork does not set `spec.chainAPI.faucet.image`. The chart renders this from `faucet.image.*`. | -| `--default-cardano-testnet-image` | `""` | Override the cardano-testnet image used for the create-env init container, the faucet source-address init container, and the default cardano-node container. Empty uses the built-in versioned reference. | +| `--default-cardano-testnet-image` | `""` | Override the cardano-testnet image used for the create-env init container and the default cardano-node container. Empty uses the built-in versioned reference. | | `--default-cardano-tools-image` | `""` | Override the cardano-tools image used for artifact staging containers. Empty uses the built-in versioned reference. | diff --git a/docs/reference/environment.md b/docs/reference/environment.md index d6e45ed2..cdfa9db3 100644 --- a/docs/reference/environment.md +++ b/docs/reference/environment.md @@ -109,7 +109,6 @@ spec.network.node.port must be set explicitly in developer config | `spec.network.local.genesis` | `spec.network.local.genesis.profile` | | `spec.network.chainAPI.ogmios` | `...ogmios.enabled`, `...ogmios.image`, `...ogmios.port` | | `spec.network.chainAPI.kupo` | `...kupo.enabled`, `...kupo.image`, `...kupo.port` | -| `spec.network.chainAPI.faucet` | `...faucet.enabled`, `...faucet.port`, `...faucet.defaultSource`, `...faucet.minTopUpLovelace`, `...faucet.maxTopUpLovelace` | These rules cover only *presence* in the source. The accepted values, types, and defaults for each field are documented in the diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 520efdc7..c0716535 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -38,8 +38,8 @@ The per-component conditions a `CardanoNetwork` publishes are `NodeReady` (primary node container running), `NodeSynchronized` (caught up to the inferred network tip), `NodeProgressing` (tip advancing or already synchronized), `ArtifactsReady` (artifact bundle staged and served over HTTP), `OgmiosReady`, -`KupoReady`, `FaucetReady`, `WalletReady`, and `DBSyncAttachmentReady` (a -primary-sidecar db-sync attachment is not blocking the primary Pod). See the +`KupoReady`, and `DBSyncAttachmentReady` (a primary-sidecar db-sync attachment +is not blocking the primary Pod). See the [CardanoNetwork reference](reference/cardanonetwork.md) for the full list. For machine-readable status, including the `reason` and `message` of every @@ -186,53 +186,31 @@ setting both, or neither, is rejected at admission with `exactly one of database.external or database.managed must be set`. See the [CardanoDBSync reference](reference/cardanodbsync.md) for the database fields. -## Faucet on a public network or faucet without Kupo +## Wallet funding fails -!!! warning "The faucet is local-only" - The faucet spends from generated devnet UTxOs, so it only makes sense on a - `local` network. Its top-up endpoint and the bearer token it requires are - host-only. +**Symptom.** `yacd wallet add --topup` or `yacd wallet topup` fails to build, +submit, or confirm a funding transaction. -**Symptom.** A network with `spec.chainAPI.faucet.enabled: true` never publishes -a faucet endpoint (`yacd info` shows `faucet: unavailable`), `FaucetReady` does -not become `True`, or `WalletReady: False` for a network that enables the -developer wallet. +**Cause and fix.** -**Cause.** Two related misconfigurations: +- **No `faucet` wallet to fund from.** Only `local` networks get a + genesis-funded `faucet` wallet. On a public network there is nothing to spend + from by default — fund the target with `--from` a wallet you have funded + yourself. +- **The source wallet has insufficient funds.** Funding spends real UTxOs. Check + the source with `yacd wallet list `, then top it up (or use `--from` a + funded wallet) before funding others. +- **`--await` times out.** Confirmation polls Kupo. If the network is still + syncing or `KupoReady` is `False`, the output may not appear in time; raise + `--await-timeout` or retry once the network is Ready. -- The faucet is enabled on a `public` network. The faucet has no generated - source UTxOs to spend on a public profile, so it cannot serve top-ups. -- The developer wallet is enabled without the faucet (and Kupo). The wallet is - bootstrapped by funding it through the faucet, so `spec.chainAPI.wallet` - requires both `spec.chainAPI.faucet` and `spec.chainAPI.kupo` to be enabled, - and is supported in local mode only. `wallet.fundingLovelace` must also not - exceed the faucet's `maxTopUpLovelace` (default `10000000000`). - -**Fix.** Use the faucet and wallet only on a local network, and enable Kupo -alongside the wallet: - -```yaml -spec: - mode: local - chainAPI: - kupo: - enabled: true - faucet: - enabled: true - wallet: - enabled: true - fundingLovelace: 100000000000 -``` - -To submit a top-up against a running local faucet, use -[`yacd topup NAME LOVELACE --address ADDR`](reference/cli.md); it self-forwards -the faucet, so no `yacd run` wrapper is needed. Add `--await` to wait for -on-chain confirmation (this needs Kupo, which is enabled by default). +See the [funding guide](developer/funding.md) and the +[`yacd wallet` reference](reference/cli.md#wallet). ## Image pull issues **Symptom.** A Pod is stuck `ImagePullBackOff` or `ErrImagePull`, and the owning -condition (`NodeReady`, `OgmiosReady`, `KupoReady`, `FaucetReady`, or +condition (`NodeReady`, `OgmiosReady`, `KupoReady`, or `PostgresReady`/`DBSyncReady`) stays `False`. **Cause.** Kubernetes could not pull a container image. The reference is wrong, @@ -251,9 +229,6 @@ so a pull failure usually means a custom override is wrong: - `spec.node.image` / `spec.node.version` (the controller derives the node image from `version`, default `11.0.1`, when `image` is unset). - `spec.chainAPI.ogmios.image` and `spec.chainAPI.kupo.image` (pinned defaults). -- `spec.chainAPI.faucet.image` — an override must use the **same repository** as - the controller's configured default faucet image; only the tag or digest may - vary. - `spec.public.bootstrap.mithril.image` for the Mithril client. - For db-sync: `spec.image` and `spec.database.managed.image` (default `postgres:17.2-alpine`). From 52488163e63935d05a18ce758bfd5aa8d65b2ad9 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Thu, 4 Jun 2026 19:31:47 -0700 Subject: [PATCH 8/9] docs: wallet list excludes the genesis faucet wallet (live-smoke fix) --- docs/developer/funding.md | 5 +++-- docs/reference/cli.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/developer/funding.md b/docs/developer/funding.md index f99a5d67..3ed89d74 100644 --- a/docs/developer/funding.md +++ b/docs/developer/funding.md @@ -54,8 +54,9 @@ yacd wallet export my-net bright-sun yacd wallet remove my-net bright-sun ``` -`list` shows each managed wallet's name, address, and source (the genesis -`faucet` wallet is included). `export` writes the wallet's `.skey`, `.vkey`, and +`list` shows the wallets you created (name, address, and the `managed-by-cli` +source); the operator-owned `faucet` wallet is not listed. `export` writes the +wallet's `.skey`, `.vkey`, and `.addr` files to `.yacd///wallets//` (override with `--out`). The `faucet` wallet is reserved and operator-owned, so the CLI will not remove it. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 88db57f1..cd8f41c3 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -294,8 +294,9 @@ and `export` accepts a managed wallet name, a public key (hex), or a bech32 yacd wallet list NET [flags] ``` -Lists the managed wallets for a network (including `faucet`) with each wallet's -name, address, and source. `--json` prints a machine-readable array. +Lists the CLI-managed wallets for a network (name, address, and the +`managed-by-cli` source); the operator-owned `faucet` wallet is not listed. +`--json` prints a machine-readable array. ### wallet add From 9a7291a84fb81179799db389ce806c65b702c12a Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Fri, 5 Jun 2026 23:04:32 -0700 Subject: [PATCH 9/9] docs: external-access URLs for chain APIs (sessions 060+062), v0.2.1 Sessions 060 (design + P1) and 062 (P2+P3, release v0.2.1) added directly reachable chain-API access so the CLI can skip the per-command port-forward. - reference/cardanonetwork.md: added `service` (ServiceExposureSpec: type ClusterIP|NodePort, nodePort) + `externalURL` to the ogmios/kupo spec, an "External access" explainer, and `externalURL` on ServiceEndpointStatus. - reference/cli.md: `--ogmios-url`/`--kupo-url` on wallet add/topup; a new "Chain access resolution" section (flag > YACD_*_URL env > probed externalURL > port-forward); run/devnet/YACD_* updated (devnet now exposes Ogmios/Kupo on localhost:1337/1442; the env vars are inputs too). - developer/connecting-tools.md + getting-started.md + concepts/architecture.md: the resolver / host-reachable devnet narrative (also dropped a stale "faucet" from the run-forwards list missed in the faucet-removal pass). - environment.md: noted the optional ogmios/kupo service/externalURL fields. - troubleshooting.md: devnet host-port collision + unreachable-externalURL note. - Version refs bumped 0.2.0 -> 0.2.1. Verified: mkdocs build --strict clean; residual sweep clean; wallet add|topup --help show the new flags. Live smoke pending. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/concepts/architecture.md | 23 +++++++----- docs/developer/connecting-tools.md | 28 ++++++++------- docs/developer/getting-started.md | 6 ++++ docs/operator/installation.md | 4 +-- docs/reference/cardanonetwork.md | 26 +++++++++++++- docs/reference/cli.md | 56 ++++++++++++++++++++++++------ docs/reference/environment.md | 5 +++ docs/troubleshooting.md | 20 +++++++++++ 8 files changed, 135 insertions(+), 33 deletions(-) diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index f2593202..58c3d7ab 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -155,14 +155,21 @@ That is correct for in-cluster consumers and for supporting controllers, but a developer's tests and tools usually run on the host. The CLI's host-access verbs bridge that gap. -`run`, `connect`, and `exec` are the three ways across the boundary. `run` and -`connect` both establish supervised port-forwards from the cluster's chain-API -Services to loopback on your host, but they surface those loopback URLs -differently. `run` wraps a single command (the primary CI path), exposing the -loopback URLs through a small `YACD_*` environment contract so tests read -ordinary environment variables instead of parsing a YACD file. `connect` holds -the forwards open in one terminal while you work in another, writing the -loopback URLs to an `endpoints.json` file instead of wiring an environment. +`run`, `connect`, and `exec` are the three ways across the boundary. `run` wraps +a single command (the primary CI path), exposing host-usable Ogmios/Kupo URLs +through a small `YACD_*` environment contract so tests read ordinary environment +variables instead of parsing a YACD file. `connect` holds supervised +port-forwards open in one terminal while you work in another, writing the URLs to +an `endpoints.json` file instead of wiring an environment. + +`run` and the wallet funding commands do not always port-forward. A +CardanoNetwork can advertise a directly reachable `externalURL` for Ogmios and +Kupo — `yacd devnet` pins one on `localhost` (a NodePort mapped to a host port), +and a platform team can front a shared cluster with an ingress and declare its +URL once. The CLI probes that URL and uses it when reachable, falling back to an +ephemeral port-forward otherwise; `connect` always forwards. Port-forwarding +stays the universal transport because it assumes nothing about how the cluster is +reached. `exec` is different, and it exists because of the socket constraint above. Tools that speak a network protocol can be forwarded, but `cardano-cli` reaches the diff --git a/docs/developer/connecting-tools.md b/docs/developer/connecting-tools.md index 28af94ba..42870218 100644 --- a/docs/developer/connecting-tools.md +++ b/docs/developer/connecting-tools.md @@ -16,20 +16,23 @@ Pick the verb by how your tool reaches the node and how long you need access: | You want to… | Use | Why | |---|---|---| -| Run a test suite or one-off command against Ogmios, Kupo, or the faucet | [`yacd run`](#run-a-test-runner) | Forwards the TCP Services to loopback ports, injects `YACD_*`, runs your command, then tears the forwards down. | +| Run a test suite or one-off command against Ogmios or Kupo | [`yacd run`](#run-a-test-runner) | Resolves a host-usable URL per endpoint (a reachable `externalURL` or a loopback port-forward), injects `YACD_*`, runs your command, then tears any forwards down. | | Run `cardano-cli` or any tool that needs the node's Unix socket | [`yacd exec`](#run-a-socket-bound-tool) | Runs the command inside the node Pod, where the socket is local. A port-forward cannot expose a Unix socket. | | Keep endpoints open for a dev server, REPL, or repeated IDE test runs | [`yacd connect`](#hold-forwards-open) | Holds supervised forwards open in one terminal and writes the loopback URLs to `endpoints.json` for other processes to read. | -The deciding factor between `run` and `exec` is the transport: **`run` forwards -TCP** (Ogmios, Kupo, faucet) to loopback ports; **`exec` runs in the Pod** for -anything that speaks to the node over its local Unix socket. +The deciding factor between `run` and `exec` is the transport: **`run` reaches +the TCP chain APIs** (Ogmios and Kupo) from the host; **`exec` runs in the Pod** +for anything that speaks to the node over its local Unix socket. ## Run a test runner -Use `yacd run NAME -- ` for the primary test/CI path. It establishes scoped -port-forwards, injects the `YACD_*` environment, runs `` on the host, and -tears the forwards down when it exits. The command's exit code is propagated, so -a test failure survives the wrapper. +Use `yacd run NAME -- ` for the primary test/CI path. It resolves a +host-usable URL for Ogmios and Kupo — preferring a reachable `externalURL` (for +example `yacd devnet`'s `localhost` ports) and falling back to a scoped +port-forward — injects the `YACD_*` environment, runs `` on the host, and +tears any forwards down when it exits. The command's exit code is propagated, so +a test failure survives the wrapper. The full resolution order is in the +[CLI reference](../reference/cli.md#chain-access-resolution). Put `--` before any command that takes its own flags so they pass through to the command instead of being parsed by `yacd`: @@ -41,8 +44,8 @@ yacd run my-net -- go test ./e2e/... Your test code reads the loopback URLs from the environment: ```go -ogmios := os.Getenv("YACD_OGMIOS_URL") // ws://127.0.0.1: -kupo := os.Getenv("YACD_KUPO_URL") // http://127.0.0.1: +ogmios := os.Getenv("YACD_OGMIOS_URL") // reachable ws:// URL (externalURL or loopback forward) +kupo := os.Getenv("YACD_KUPO_URL") // reachable http:// URL ``` With no command, `run` drops into your `$SHELL` (falling back to `/bin/sh`) with @@ -128,8 +131,9 @@ while `connect` is running. For the document's field names and shape, see the `yacd wallet topup NET WALLET LOVELACE --await` funds a target and polls Kupo until the funding transaction's output appears, so a test never races chain inclusion. The `WALLET` argument is a managed wallet name or a bech32 -`addr_test...` address, and `topup` forwards Ogmios and Kupo itself — no `yacd -run` wrapper and no URL flags: +`addr_test...` address, and `topup` reaches Ogmios and Kupo on its own (a +reachable `externalURL` or a port-forward), so no `yacd run` wrapper is needed. +Pass `--ogmios-url` / `--kupo-url` to point it at specific endpoints: ```sh export ADDR=addr_test1... # the address your test funds diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index e7c9a80e..9471679b 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -103,6 +103,12 @@ yacd info devnet connect to. Keep this command handy while you work; it is the quickest way to check what a network is exposing. +!!! tip "devnet is reachable on localhost" + `yacd devnet` maps Ogmios and Kupo to fixed host ports, so you can point a + tool straight at `ws://localhost:1337` (Ogmios) and `http://localhost:1442` + (Kupo) — no port-forward. `yacd run` and the wallet commands use these + automatically. + ## 4. Query the chain tip Run `cardano-cli` *inside* the node Pod to query the current chain tip. This is diff --git a/docs/operator/installation.md b/docs/operator/installation.md index 255b57c4..6f4e85de 100644 --- a/docs/operator/installation.md +++ b/docs/operator/installation.md @@ -25,7 +25,7 @@ This page covers installing onto an existing cluster. For a local [k3d](https:// === "Helm" - Install the chart into a namespace, creating it if it does not exist. Replace `` with the release version you want (for example `0.2.0`) and `` with your target namespace. + Install the chart into a namespace, creating it if it does not exist. Replace `` with the release version you want (for example `0.2.1`) and `` with your target namespace. ```sh helm install yacd oci://ghcr.io/meigma/yacd/chart \ @@ -34,7 +34,7 @@ This page covers installing onto an existing cluster. For a local [k3d](https:// --create-namespace ``` - The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.2.0` ships `appVersion` `v0.2.0`). To inspect the chart metadata before installing: + The chart name is `chart` and its version tracks the release version without the leading `v` (the published chart `0.2.1` ships `appVersion` `v0.2.1`). To inspect the chart metadata before installing: ```sh helm show chart oci://ghcr.io/meigma/yacd/chart --version diff --git a/docs/reference/cardanonetwork.md b/docs/reference/cardanonetwork.md index 07d0491e..f6c963a6 100644 --- a/docs/reference/cardanonetwork.md +++ b/docs/reference/cardanonetwork.md @@ -188,6 +188,8 @@ enabled by default. | `enabled` | boolean | yes | `true` | — | Whether the Ogmios sidecar is deployed. | | `image` | string | yes | `cardanosolutions/ogmios:v6.14.0` | — | Ogmios image reference. | | `port` | integer (int32) | yes | `1337` | `1`–`65535` | Ogmios service port. | +| `service` | [ServiceExposureSpec](#serviceexposurespec) | no | — | — | How the Ogmios Service is exposed (ClusterIP or NodePort). | +| `externalURL` | string | no | — | — | Operator-asserted externally reachable URL, mirrored to `status.endpoints.ogmios.externalURL`. See [external access](#external-access). | | `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Ogmios container resources. | #### spec.chainAPI.kupo @@ -197,8 +199,29 @@ enabled by default. | `enabled` | boolean | yes | `true` | — | Whether the Kupo sidecar is deployed. | | `image` | string | yes | `cardanosolutions/kupo:v2.11.0` | — | Kupo image reference. | | `port` | integer (int32) | yes | `1442` | `1`–`65535` | Kupo service port. | +| `service` | [ServiceExposureSpec](#serviceexposurespec) | no | — | — | How the Kupo Service is exposed (ClusterIP or NodePort). | +| `externalURL` | string | no | — | — | Operator-asserted externally reachable URL, mirrored to `status.endpoints.kupo.externalURL`. See [external access](#external-access). | | `resources` | [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core) | no | — | — | Kupo container resources. | +#### ServiceExposureSpec + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `type` | string enum | no | `ClusterIP` | `ClusterIP`, `NodePort` | Kubernetes Service type for the endpoint. `NodePort` additionally exposes it on a static node port. | +| `nodePort` | integer (int32) | no | — | `≤ 32767` | Pins the node port when `type` is `NodePort`. `0` or omitted auto-assigns from the node-port range (30000–32767); a pinned value is valid only with `NodePort`. | + +#### External access + +`service` and `externalURL` let a network advertise a directly reachable address so +the CLI can skip the per-command port-forward (see +[Connecting tools](../developer/connecting-tools.md)). They are an +**advertisement**: the operator publishes `externalURL`, but making it routable is +the provisioner's job — `NodePort` plus a host-port mapping for `yacd devnet`, or an +ingress for a shared cluster. `externalURL` is validated leniently (an absolute URL +with a host and a `ws`, `wss`, `http`, or `https` scheme) because the CLI probes it +for reachability before trusting it. The in-cluster `url` in status is unchanged; +`externalURL` is an additive peer. + !!! note "No faucet field — funding is CLI-native" There is no `chainAPI.faucet` or `chainAPI.wallet` field. Every local network is created with a genesis-funded `faucet` wallet, stored as a @@ -247,7 +270,8 @@ Each endpoint is a [ServiceEndpointStatus](#serviceendpointstatus). | --- | --- | --- | | `serviceName` | string | Kubernetes Service name. | | `port` | integer (int32) | Service port. | -| `url` | string | Convenience URL for protocols with a stable URL shape. | +| `url` | string | In-cluster convenience URL for protocols with a stable URL shape. | +| `externalURL` | string | Operator-asserted externally reachable URL, mirrored from the spec. Empty unless the network sets `chainAPI..externalURL`. | ### status.sync diff --git a/docs/reference/cli.md b/docs/reference/cli.md index cd8f41c3..b99ac00f 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -82,6 +82,13 @@ On success it prints the cluster context, the operator version, and (unless `--bare`) the Ogmios and Kupo endpoints, the funded wallet address, and a copy-pasteable `yacd exec` tip-query hint. +`devnet` exposes Ogmios and Kupo as NodePort Services mapped to fixed host ports, +so they are reachable from your machine at `ws://localhost:1337` (Ogmios) and +`http://localhost:1442` (Kupo) without a port-forward. The printed endpoints +advertise those host URLs, and the CLI uses them automatically (see +[chain access resolution](#chain-access-resolution)). If port 1337 or 1442 is +already in use, `devnet` fails with a clear collision error. + ### devnet down ```text @@ -144,7 +151,7 @@ the operator version is to upgrade the CLI. `--dry-run` prints the action the next install would take and changes nothing: ```text -Plan: install operator (installed none -> v0.2.0) in namespace yacd-system +Plan: install operator (installed none -> v0.2.1) in namespace yacd-system ``` See [Installation](../operator/installation.md) for the full operator-install @@ -313,6 +320,8 @@ wallet. | `--topup` | string | `""` | Fund the new wallet with this many lovelace from the faucet. | | `--await` | bool | `false` | Wait for the funding transaction to confirm on-chain (requires `--topup`). | | `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. | +| `--ogmios-url` | string | `""` | Ogmios URL to use for funding; overrides the [resolved endpoint](#chain-access-resolution). | +| `--kupo-url` | string | `""` | Kupo URL to use for funding; overrides the [resolved endpoint](#chain-access-resolution). | | `--json` | bool | `false` | Print machine-readable JSON. | ### wallet topup @@ -322,14 +331,17 @@ yacd wallet topup NET WALLET LOVELACE [flags] ``` Funds `WALLET` with `LOVELACE` (positional, must be greater than 0) from the -`faucet` wallet, or from another managed wallet with `--from`. It forwards Ogmios -and Kupo itself, so no `yacd run` wrapper or URL flags are needed. +`faucet` wallet, or from another managed wallet with `--from`. It reaches Ogmios +and Kupo on its own (see [chain access resolution](#chain-access-resolution)), so +no `yacd run` wrapper is needed. | Flag | Type | Default | Meaning | | --- | --- | --- | --- | | `--from` | string | `faucet` | Source wallet name to fund from (default: the faucet wallet). | | `--await` | bool | `false` | Wait for the funding transaction to confirm on-chain. | | `--await-timeout` | duration | `2m0s` | Maximum time to wait for `--await` confirmation. | +| `--ogmios-url` | string | `""` | Ogmios URL to use for funding; overrides the [resolved endpoint](#chain-access-resolution). | +| `--kupo-url` | string | `""` | Kupo URL to use for funding; overrides the [resolved endpoint](#chain-access-resolution). | | `--json` | bool | `false` | Print machine-readable JSON. | ### wallet export @@ -354,16 +366,35 @@ yacd wallet remove NET WALLET Deletes a managed wallet. The reserved `faucet` wallet cannot be removed. +## Chain access resolution + +`yacd run` and the wallet funding commands resolve a host-usable URL for each of +Ogmios and Kupo, preferring a directly reachable endpoint over an ephemeral +port-forward. Per endpoint, the first available rung wins: + +1. An explicit `--ogmios-url` / `--kupo-url` flag (funding commands only). +2. An ambient `YACD_OGMIOS_URL` / `YACD_KUPO_URL` (set inside `yacd run`). +3. The network's `status.endpoints..externalURL`, when a short + reachability probe (a ~2s TCP dial) succeeds. +4. An ephemeral `kubectl` port-forward — the universal fallback. + +So a network that advertises a reachable `externalURL` — `yacd devnet` pins one on +`localhost`, and a shared cluster can front one with an ingress — is reached +without a port-forward. `yacd connect` always forwards; it is the remote-access +tool. + ## run ```text yacd run NAME [-- command [args...]] [flags] ``` -Establishes scoped port-forwards to the network's chain-API endpoints, injects -the [`YACD_*` environment](#the-yacd-environment-contract), and execs the command -(or your `$SHELL`, falling back to `/bin/sh`, when none is given) on the host -with that environment. The forwards are torn down when the command exits. +Resolves a host-usable URL for each chain-API endpoint (see +[chain access resolution](#chain-access-resolution)) — using a reachable +`externalURL` when one exists and otherwise a scoped port-forward — injects the +[`YACD_*` environment](#the-yacd-environment-contract), and execs the command (or +your `$SHELL`, falling back to `/bin/sh`, when none is given) on the host with +that environment. Any forwards are torn down when the command exits. Put `--` before any command that takes its own flags so they are passed through to the command instead of being parsed by `yacd`. @@ -440,9 +471,14 @@ CardanoNetwork to be Ready. `run`, `connect`, and `exec` publish a stable, versioned set of `YACD_*` variables (contract version 1). Tests and tooling read these instead of parsing any YACD file. The variable names are identical whether a command runs on the -host (`run`, over port-forwards) or inside the primary Pod (`exec`, over cluster -DNS); only the values adapt. Adding a variable is backward compatible; renaming -or removing one is a breaking change to the contract. +host (`run`, over the resolved chain access) or inside the primary Pod (`exec`, +over cluster DNS); only the values adapt. Adding a variable is backward +compatible; renaming or removing one is a breaking change to the contract. + +`YACD_OGMIOS_URL` and `YACD_KUPO_URL` are also **inputs**: the funding commands +and a nested `yacd run` read them during +[chain access resolution](#chain-access-resolution), so a tool launched inside +`yacd run` reuses its already-resolved endpoints instead of opening its own. | Variable | Host (`run`) value | In-pod (`exec`) value | Present when | | --- | --- | --- | --- | diff --git a/docs/reference/environment.md b/docs/reference/environment.md index cdfa9db3..47215310 100644 --- a/docs/reference/environment.md +++ b/docs/reference/environment.md @@ -115,3 +115,8 @@ and defaults for each field are documented in the [CardanoNetwork reference](cardanonetwork.md). Chain-API blocks you omit entirely keep their network-spec defaults; you opt into explicit-field enforcement for a block only by including that block. + +An ogmios or kupo block may also carry the optional `service` (ClusterIP or +NodePort) and `externalURL` fields to make the endpoint reachable from outside +the cluster; these are not required-explicit. See +[external access](cardanonetwork.md#external-access). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index c0716535..cb2c2129 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -207,6 +207,26 @@ submit, or confirm a funding transaction. See the [funding guide](developer/funding.md) and the [`yacd wallet` reference](reference/cli.md#wallet). +## devnet host port already in use + +**Symptom.** `yacd devnet` fails to create its cluster with a host-port collision +error naming port `1337` or `1442`. + +**Cause.** `yacd devnet` maps Ogmios to host port `1337` and Kupo to `1442` so +they are reachable on `localhost` without a port-forward. If another process +(another devnet, a local Ogmios/Kupo, or anything else) already holds one of +those ports, k3d cannot bind it. + +**Fix.** Free the port — stop the other process, or `yacd devnet down` a previous +devnet — then re-run `yacd devnet`. + +!!! note "An advertised `externalURL` that is not reachable" + `run` and the wallet commands probe a network's `externalURL` before using + it and fall back to a port-forward when the probe fails, so an unreachable + `externalURL` only adds a brief delay, never an error. To force a specific + endpoint, pass `--ogmios-url` / `--kupo-url` (funding) or set + `YACD_OGMIOS_URL` / `YACD_KUPO_URL`. + ## Image pull issues **Symptom.** A Pod is stuck `ImagePullBackOff` or `ErrImagePull`, and the owning