# helm-kubekanvas

A [Helm post-renderer](https://helm.sh/docs/topics/advanced/#post-rendering) that decrypts **AGE-encrypted** values in rendered Kubernetes manifests before they are applied to the cluster.

This tool is part of the [KubeCanvas](https://kubekanvas.io) platform. When you design a project in KubeCanvas and the chart contains secrets, those secrets are encrypted with your workspace AGE public key and stored as `AGE-ENC:<base64>` values inside the Helm chart. This binary decrypts them at deploy time using the matching private key.

---

## How it works

`helm-kubekanvas` is invoked automatically by Helm as a post-renderer:

```
Helm renders chart → pipes YAML to helm-kubekanvas stdin → decrypted YAML written to stdout → applied to cluster
```

Values with the `AGE-ENC:` prefix are decrypted in place. All other values pass through unchanged. If no encrypted values are found, the input is returned byte-for-byte.

### Context-aware decryption

The tool understands Kubernetes resource structure:

| Resource | Field | Output |
|---|---|---|
| `kind: Secret` | `.data.*` | decrypted → **base64-encoded** (Kubernetes requires it) |
| `kind: Secret` | `.stringData.*` | decrypted → **plaintext** |
| Any other kind | any field | decrypted → **plaintext** |

---

## Installation

### As a Helm plugin (recommended)

```bash
helm plugin install https://downloads.kubekanvas.io/helm-plugin/latest/plugin.tar.gz
```

The plugin downloads the correct pre-built binary for your OS/arch. After installation the binary is available as:
- `helm-kubekanvas` (for `--post-renderer`)
- `helm kubekanvas` (as a Helm subcommand)

### Manual download

Download the binary for your platform from [GitHub Releases](https://github.com/kubekanvas/helm-kubekanvas/releases) or directly from S3:

```
https://downloads.kubekanvas.io/helm-plugin/bin/latest/
```

### Build from source

```bash
git clone https://github.com/kubekanvas/helm-kubekanvas
cd helm-kubekanvas
go build -o helm-kubekanvas .
```

With system keychain support (requires CGO):

```bash
go get github.com/zalando/go-keyring
go build -tags keychain -o helm-kubekanvas .
```

---

## Usage

### Basic

```bash
helm install myapp ./chart --post-renderer helm-kubekanvas
helm upgrade myapp ./chart --install --post-renderer helm-kubekanvas
```

### With a key file (Helm 3.7+)

```bash
helm install myapp ./chart \
     --post-renderer helm-kubekanvas \
     --post-renderer-args "--key-file /path/to/age.key"
```

### In CI/CD pipelines

```bash
export KUBECANVAS_AGE_KEY="AGE-SECRET-KEY-1..."
helm upgrade myapp ./chart --install --post-renderer helm-kubekanvas
```

---

## Key loading

The private key is resolved using the following priority chain (first match wins):

| Priority | Source | How |
|---|---|---|
| 1 | `--key-file <path>` | CLI flag: path to an AGE private key file |
| 2 | `KUBECANVAS_AGE_KEY_FILE` | Env var: path to an AGE private key file |
| 3 | `KUBECANVAS_AGE_KEY` | Env var: raw AGE private key string (`AGE-SECRET-KEY-1...`) |
| 4 | System keychain | Stored by `kubecanvas configure` in the kubecanvas-cli desktop tool |

The system keychain lookup is only available in binaries built with `-tags keychain`. Pre-built release binaries do **not** include keychain support (they are static/CGO-free). Desktop users who installed [kubecanvas-cli](https://github.com/kubekanvas/kubecanvas-cli) can build from source with `-tags keychain` to use the key the CLI stored in their keychain.

---

## Encrypted value format

Values encrypted by the KubeCanvas platform follow this format:

```
AGE-ENC:<base64-encoded AGE binary ciphertext>
```

The base64 payload is the raw AGE binary file format (not ASCII-armored). Encryption uses X25519 recipients.

---

## Flags

| Flag | Description |
|---|---|
| `--key-file <path>` | Path to an AGE private key file |
| `--version` | Print version and exit |

---

## Error handling

If decryption fails (wrong key, malformed value, key not found), the tool:
1. Writes the **original unmodified input** back to stdout
2. Writes the error to stderr
3. Exits with code 1

This prevents Helm from hanging on an empty stdout and ensures a clean, visible failure.

---

## Platform support

| OS | Arch | Pre-built binary |
|---|---|---|
| Linux | amd64 | ✅ |
| Linux | arm64 | ✅ |
| macOS | amd64 | ✅ |
| macOS | arm64 (Apple Silicon) | ✅ |
| Windows | amd64 | ✅ |

---

## Related

- [kubecanvas-cli](https://github.com/kubekanvas/kubecanvas-cli) — The desktop CLI that manages kubecanvas authentication, cluster connections, and encrypts chart values using your workspace AGE key pair.

---

## Local development

### Prerequisites

| Tool | Purpose |
|---|---|
| Go 1.21+ | Build the binary |
| `helm` | Run integration tests |
| `age` / `age-keygen` | Encrypt test values in the test script |

Install `age` on Linux: `apt install age` or download from [github.com/FiloSottile/age/releases](https://github.com/FiloSottile/age/releases).

### 1. Clone and build

```bash
git clone https://github.com/kubekanvas/helm-kubekanvas
cd helm-kubekanvas
go build -o helm-kubekanvas .
```

### 2. Run unit tests

No tools required — uses ephemeral AGE keys generated at test time:

```bash
go test ./... -v
```

### 3. Install as a Helm plugin (from local source)

Helm detects the presence of `go.mod` and builds from source automatically — no release required:

```bash
helm plugin install /path/to/helm-kubekanvas
```

The install hook builds the binary, installs it to the plugin's `bin/` directory, and symlinks it to `~/.local/bin/helm-kubekanvas` so Helm can find it by name.

To reinstall after code changes:

```bash
helm plugin remove kubekanvas
helm plugin install /path/to/helm-kubekanvas
```

### 4. End-to-end test with a real chart

The included script generates a throwaway AGE keypair, creates a minimal chart with encrypted values, runs `helm template --post-renderer`, and asserts the output:

```bash
./scripts/test-local.sh

# Keep the generated chart in /tmp for inspection:
./scripts/test-local.sh --keep
```

### 5. Test against your own chart

```bash
export KUBECANVAS_AGE_KEY="AGE-SECRET-KEY-1..."
helm install myapp ./my-chart --post-renderer helm-kubekanvas
# or dry-run without a cluster:
helm template myapp ./my-chart --post-renderer helm-kubekanvas
```
