> ## Documentation Index
> Fetch the complete documentation index at: https://timoni.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Bundle Secrets Injection

> Inject secrets from Kubernetes, CI envs, 1Password or SOPS.

Ways of injecting secrets when deploying applications with Timoni [Bundles](/bundle):

* Using [runtime](#runtime-secrets) attributes `@timoni(runtime:string:SECRET-NAME)`.
* Using [1Password](#1password-secrets) secret references resolved by the `op` CLI.
* Using [SOPS](#sops-secrets) encrypted YAML or JSON files.

## Runtime Secrets

To showcase how secrets injections works, we'll assume we are deploying an application
that connects to an S3-compatible API and needs two secrets: `ACCESS_KEY` and `SECRET_KEY`.

### Injecting secrets from CI secret store

When using a CI runner to deploy apps with Timoni,
we can pass secrets from the runner's secret store to Timoni's Bundle.

Example of a Bundle that contains runtime attributes:

```cue theme={"system"}
bundle: {
	apiVersion: "v1alpha1"
	name:       "my-app"
	instances: {
		"my-app-storage": {
			module: url: "oci://my-registry/timoni/modules/my-app-storage"
			namespace: "my-app"
			values: {
				endpoint:  "https://my-acc.r2.cloudflarestorage.com"
				accessKey: string @timoni(runtime:string:ACCESS_KEY)
				secretKey: string @timoni(runtime:string:SECRET_KEY)
			}
		}
	}
}
```

In a GitHub workflow, we can map secrets from GitHub secrets to env vars,
that Timoni will use at apply-time:

```shell theme={"system"}
export ACCESS_KEY=${{ secrets.ACCESS_KEY }}
export SECRET_KEY=${{ secrets.SECRET_KEY }}

timoni bundle apply -f bundle.cue --runtime-from-env
```

### Injecting secrets from Kubernetes

The same secrets from the above example, can be injected from a Kubernetes Secret,
assuming we're using some external-secret controller that syncs secrets from a Vault in etcd.

Example of a Timoni [Bundle Runtime](/bundle-runtime) that fetches the secrets from the cluster:

```cue theme={"system"}
runtime: {
	apiVersion: "v1alpha1"
	name:       "production"
	values: [
		{
			query: "k8s:v1:Secret:my-namespace:my-secret-name"
			for: {
				"ACCESS_KEY": "obj.data.r2_access_key"
				"SECRET_KEY": "obj.data.r2_secret_key"
			}
		},
	]
}
```

At apply-time we pass the runtime definition and
Timoni will read the secrets from the Kubernetes cluster
and use them when applying the bundle:

```shell theme={"system"}
timoni bundle apply -f bundle.cue --runtime runtime.cue
```

### Secrets interpolation

When the secrets stored in external system do not map to a value key in the Bundle,
we can use CUE's string interpolation to compose the desired value.

Assuming an application config expects a Redis URL, but the secret store contains
`REDIS_HOST` and `REDIS_PASS`.

```cue theme={"system"}
bundle: {
	apiVersion: "v1alpha1"
	name:       "podinfo"
	_secrets: {
		host:     string @timoni(runtime:string:REDIS_HOST)
		password: string @timoni(runtime:string:REDIS_PASS)
	}
	instances: {
		"podinfo-backend": {
			module: url: "oci://ghcr.io/stefanprodan/modules/podinfo"
			namespace: "podinfo"
			values: caching: {
				enabled:  true
				redisURL: "tcp://:\(_secrets.password)@\(_secrets.host):6379"
			}
		}
	}
}
```

In the above example, we define a CUE hidden field `_secrets`, where we set the
runtime secrets mappings. Then in the instance values, we use string interpolation
to set the `redisURL` containing the secrets.

Using the build command, we can see the URL value set in the podinfo container args:

```console theme={"system"}
$ export REDIS_HOST=redis.svc
$ export REDIS_PASS=testpass
$ timoni bundle build -f bundle.cue --runtime-from-env | grep redis
        - --cache-server=tcp://:testpass@redis.svc:6379
```

## 1Password secrets

When using [1Password](https://developer.1password.com/docs/cli/secrets-environment-variables/),
the `op run` command resolves secret references to env vars and passes them
to Timoni as a subprocess, where they are read with `--runtime-from-env`.

### Injecting secrets with op CLI

Using the bundle from the [CI example](#injecting-secrets-from-ci-secret-store),
create an env file `secrets.env` that maps the runtime attribute names
to 1Password secret references:

```shell theme={"system"}
ACCESS_KEY=op://my-vault/my-app-storage/access-key
SECRET_KEY=op://my-vault/my-app-storage/secret-key
```

At apply-time, run Timoni with `op run`:

```shell theme={"system"}
op run --env-file=secrets.env -- timoni bundle apply -f bundle.cue --runtime-from-env
```

The `op` CLI authenticates with the 1Password desktop app on workstations,
or with a service account in CI when the `OP_SERVICE_ACCOUNT_TOKEN` env var is set.

To inspect the generated manifests, run `timoni bundle build` with `op run`.
The secret values are masked in the output as `<concealed by 1Password>`:

```shell theme={"system"}
op run --env-file=secrets.env -- timoni bundle build -f bundle.cue --runtime-from-env
```

## SOPS secrets

When using [SOPS](https://github.com/getsops/sops),
we can decrypt the secrets and [inject](https://github.com/getsops/sops#passing-secrets-to-other-processes)
those values to env vars, then use `--runtime-from-env`.

Another option is to extract the secret values of a Timoni Bundle to a YAML or JSON file,
that we encrypt/decrypt with SOPS.

### Injecting secrets from SOPS

Main bundle file `bundle.main.cue`:

```cue theme={"system"}
bundle: {
	apiVersion: "v1alpha1"
	name:       "my-app"
	instances: {
		"my-app-storage": {
			module: url: "oci://my-registry/timoni/modules/my-app-storage"
			namespace: "my-app"
			values: {
				endpoint: "https://my-acc.r2.cloudflarestorage.com"
				// The secrets are omitted here!
			}
		}
	}
}
```

Bundle partial in YAML format `bundle.secret.yaml`:

```yaml theme={"system"}
bundle:
  instances:
    my-app-storage:
      values:
        accessKey: ENC[AES256_GCM,data:..]
        secretKey: ENC[AES256_GCM,data:..]
```

Assuming the `bundle.secret.yaml` file is kept encrypted with SOPS,
at apply-time we can run the SOPS decryption,
and pass the plain YAML to Timoni's apply command like so:

```shell theme={"system"}
sops exec-file --filename secrets.yml bundle.secret.yaml 'timoni bundle apply -f bundle.main.cue -f {}'
```
