@codiac.io/codiac-cli 1.3.245 → 1.3.246

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ # `cod` CLI Reference (orientation)
2
+
3
+ **This is orientation, not the source of truth.** `cod <topic> <verb> --help` is authoritative —
4
+ it carries the exact current flags, their descriptions, and copy-pasteable examples. Use this file
5
+ to (a) confirm a command *exists* and its noun-then-verb shape, and (b) stay alert to the traps
6
+ below. Then `--help` the specific command and adapt one of its examples.
7
+
8
+ ## Command grammar
9
+
10
+ - **Noun-then-verb, always.** `cod asset deploy`, `cod cluster stack install`,
11
+ `cod config settings get`. Never verb-first (`cod deploy asset` ✗), never reordered.
12
+ - Directory-to-command mapping: `<topic>/<verb>` → `cod <topic> <verb>`; deeper nesting works too
13
+ (`asset probe list`).
14
+ - A few real **camelCase** topics/verbs exist despite the single-word convention:
15
+ `imageRegistry`, `secretStore`, `pullSecret`, and `auth ... allow onEnvironments`/`onCabinets`.
16
+
17
+ ## Command tree (what actually exists — don't invent beyond this)
18
+
19
+ Top-level: `branch`, `build`, `cli`, `commit`, `deploy`, `dep`, `identity`, `init`, `login`,
20
+ `logout`, `merge`, `portal`, `publish`, `pull`, `push`, `relay-version`, `run`, `stage`, `stash`,
21
+ `status`, `stop`, `switch`, `sync`, `unstage`, `whereami`, `whoami`.
22
+
23
+ - **ai**: `ai agent install`
24
+ - **asset**: `create`, `deploy`, `edit`, `list`, `obliterate`, `recycle`, `undeploy`, `view`, `where`; `entrypoint set/delete`; `probe create/delete/list`; `volume create/delete/list`
25
+ - **auth**: `register`; `cabinet allow/allowed`; `enterprise allow/allowed/allow onEnvironments`; `environment allow/allowed/allow onCabinets`; `role create/delete/list/assign/unassign/allowed`; `user invite/list`
26
+ - **cabinet**: `create`, `list`, `define`, `contents`, `destroy`, `detach`, `forget`, `obliterate`, `restore`; `cluster attach`
27
+ - **cluster**: `connect`, `install`, `bootstrap`, `start`, `stop`; `agent configure/install/uninstall`; `scaling update`; `stack capture/install`
28
+ - **config**: `add`, `delete`, `deploy`, `import`, `view`; `settings get`
29
+ - **csp**: `login`, `logout`, `setup`, `creds list`
30
+ - **enterprise**: `create`, `list`
31
+ - **environment**: `create`, `delete`, `list`; `cluster attach/detach`
32
+ - **env**: `add` *(separate topic from `environment`)*
33
+ - **filestore**: `capture`, `forget`, `list`
34
+ - **host**: `map`, `list`, `view`, `delete`
35
+ - **identity**: `list`, `delete`
36
+ - **imageRegistry**: `capture`, `forget`, `list`; `pullSecret set/unset`
37
+ - **images**: `add`, `list`, `patch`, `remove`, `version`, `version bump`
38
+ - **import**: `cluster`
39
+ - **kit**: `create`
40
+ - **noc**: `noc cluster create/list/init/deinit/capture/restore/destroy/define/forget/grant/journal/credentials-set`
41
+ - **pkg**: `add`, `list`, `patch`, `remove`
42
+ - **pvc**: `create`, `get`, `list`, `delete`
43
+ - **secretStore**: `capture`, `forget`, `list`
44
+ - **snapshot**: `deploy`, `list`, `tag`, `tags`, `untag`
45
+ - **tenant**: `create`, `switch`
46
+
47
+ If a task seems to need a command not on this list, you're probably guessing — re-check the tree
48
+ or `--help` the parent topic (`cod asset --help`, `cod config --help`) to see its verbs.
49
+
50
+ ## Interaction-mode flags (on nearly every command)
51
+
52
+ | Flag | Meaning |
53
+ |---|---|
54
+ | `--silent` | Non-interactive. Runs with zero prompts and **throws on any missing/invalid required arg** — there is no prompt fallback. Pass every required value. |
55
+ | `--echo` | Prints the equivalent `--silent` command string, then runs it. **Runs the interactive build first → prompts and HANGS in a non-TTY.** Interactive terminals only. |
56
+ | `--to-script` | Prints the equivalent `--silent` command string without running it. Same trap: it does the interactive build first, so it **prompts and HANGS headless.** Not a headless dry-run — use it only in a real terminal. Headless, use `--silent` (throws on missing args, never hangs) and build the command from `--help`. |
57
+ | `-h` / `--help` | Help + examples. (A couple of commands don't define it — see traps.) |
58
+ | `--take-defaults` | Auto-accept default/context values without confirmation. |
59
+
60
+ - The mode flags have **no char shortcut**; spell them exactly (`--silent`).
61
+ - **Piped stdin auto-switches to silent** ("Piped input detected…"), so a piped command needs all
62
+ args too. Some commands read secrets from stdin in silent mode (`csp login` password,
63
+ `config add --value-stdin`).
64
+
65
+ ## The flag-character collision map (why you must `--help`)
66
+
67
+ The same char means different things per command. **Never assume.**
68
+
69
+ | Char | `asset deploy` | `asset view` | `config add` / `settings get` | `config view` | `csp login` | `cabinet create` | `ai agent install` |
70
+ |---|---|---|---|---|---|---|---|
71
+ | `-e` | enterpriseCode | enterprise | **enviro (environment)** | enterprise | — | **environment** | enterpriseCode |
72
+ | `-n` | **provider username** | **environment** | **enterprise** | — | — | — | **workload name** |
73
+ | `-u` | **version** | — | — | — | user | — | — |
74
+ | `-t` | cabinet **tag** | **type** (content) | config **type** | config type | **tenantId** | — | agent **type** |
75
+ | `-s` | applySnapshotTag | — | **enterprise-scope** (bool) | — | subscriptionId | — | — |
76
+ | `-c` | cabinetName | cabinet | cabinet | cabinet | — | **cluster** | cabinet |
77
+ | `-a` | asset | asset | asset | asset | — | — | — |
78
+ | `-g` | applyAssetTag | — | — | — | — | — | ingress (`--no-ingress`) |
79
+
80
+ Highlights that bite most often:
81
+ - **`-e` is enterprise in some commands, environment in others.** In `config add` /
82
+ `config settings get`, enterprise is **`-n`** and `-e` is **`--enviro`**. `config view` uses
83
+ `-e` = enterprise. Verify per command every time.
84
+ - **Asset version is `-u`, not `-v`.**
85
+ - On `asset deploy`, `-n` = cloud username and `-t` = cabinet tag (not enterprise, not type).
86
+
87
+ ## Positional args (not flags)
88
+
89
+ Several primary inputs are positional — passing them as `--flag` fails:
90
+
91
+ | Command | Positional |
92
+ |---|---|
93
+ | `cod tenant switch <code>` | tenant code |
94
+ | `cod cluster connect <cluster>` | cluster name |
95
+ | `cod csp login <provider>` | provider (`aws`/`azure`/`docker`) |
96
+ | `cod cabinet create <cabinet>` | cabinet name (env/cluster are flags: `--environment`, `--cluster`) |
97
+ | `cod config view [setting]` | optional setting name |
98
+
99
+ `csp login` and `cluster connect` don't even define `-h` — read their source/behavior, not `--help`.
100
+
101
+ ## List / read commands
102
+
103
+ Commands that return a list support:
104
+ - `--output` / `-o` — `tree` (default, human) · `json` · `yaml` · sometimes `text`/`tsv`/`csv`.
105
+ - `--query` / `-q` — JMESPath, applied to the raw array. `-q` implies JSON; **`tree`/`text`
106
+ reject `-q`** (throws).
107
+
108
+ Examples: `cod asset list -e main -q '[].name' -o json` · `cod cabinet list -e main -o yaml`.
109
+ Exception: `cod config settings get` has **no `-q`** — filter with `--setting`/`--value` globs — and
110
+ defaults to `json`. `cod config view` uses a different format enum (`pretty`/`json`/`yaml`/`auto`).
111
+
112
+ ## `cod asset view` — operating a running workload (no kubectl)
113
+
114
+ Flags: `-e` enterprise · `-a` asset · `-n` **environment** · `-c` cabinet · `-r` replica (`any` =
115
+ first, no prompt) · `-t` content type. The `-t` (`AssetContentType`) values are exactly:
116
+
117
+ | `-t` | What it does | Headless-safe? |
118
+ |---|---|---|
119
+ | `logs` | container logs; `-l N` lines (`-l -1` = all); `-f` follows (streams forever — omit headless) | ✅ with `-l N`, no `-f` |
120
+ | `env` | resolved env vars | ✅ |
121
+ | `config` | resolved config (`-T <helm\|env\|label\|annotation\|…>` or `-F <file>`) | ✅ |
122
+ | `exec` | run a command in a replica (positional cmd after `--`) | interactive |
123
+ | `term` | shell into a replica | interactive only |
124
+ | `events` | recent k8s events for the asset (returns + exits; `-l N` count, default 25) | ✅ **use this for headless events** |
125
+ | `status` | **live streaming monitor** (aliases `asset:monitor`/`asset:mon`) — status + events, updating | ❌ **never returns in a non-TTY** (interactive only) |
126
+
127
+ For events in a script use **`cod asset view -t events`** — it fetches once, prints, and exits
128
+ (unlike `-t status`, which streams). It works even when pods aren't up (that's when events matter:
129
+ ImagePullBackOff, FailedScheduling, CrashLoopBackOff), and works for `helm` assets too, not just
130
+ `service`. Example: `cod asset view -t events -e main -a hello-web -n dev -c demo -l 20 --silent`.
131
+ For a plain status snapshot, `cod cabinet contents` / `cod asset list` also return and exit. No
132
+ `kubectl` needed.
133
+
134
+ ## A few verified recipes (adapt via `--help`)
135
+
136
+ ```bash
137
+ # switch tenant (positional code)
138
+ cod tenant switch ben --silent
139
+
140
+ # preview the resolved config a cabinet will deploy with (best "what will happen?" check)
141
+ cod config view -e main -a hermes-agent -c dev -o pretty --silent
142
+
143
+ # set a helm value at enterprise scope (note: -n = enterprise, -e would be enviro here)
144
+ cod config set -n main -a hermes-agent -t helm --setting model --value 'openai/gpt-4o-mini' -s --silent
145
+
146
+ # inspect raw scoped settings (is it set, and where?)
147
+ cod config settings get -n main -a hermes-agent -t helm -o json --silent
148
+
149
+ # create a cabinet in an ENVIRONMENT (the target is --environment; --cluster is OPTIONAL and
150
+ # defaults to the environment's first cluster — only pass it to disambiguate a multi-cluster env)
151
+ cod cabinet create dev --enterprise main --environment dev --silent
152
+
153
+ # what's deployed in a cabinet (headless-safe)
154
+ cod cabinet contents -e main -c dev --silent
155
+
156
+ # deploy an asset version into a cabinet (version is -u!)
157
+ cod asset deploy -e main -a my-service -u 1.0.0 -c dev --silent
158
+
159
+ # cloud/registry auth (provider positional; secret via stdin)
160
+ cat pw.txt | cod csp login -u me@example.com --silent docker
161
+
162
+ # connect kubectl to a Codiac cluster (positional name)
163
+ cod cluster connect my-cluster --silent
164
+ ```
165
+
166
+ Every one of these was read from source, but flags evolve — if a recipe errors, `--help` the
167
+ command and re-derive it. That habit, not this file, is what keeps you correct.
@@ -0,0 +1,244 @@
1
+ # Codiac Concept Model
2
+
3
+ The mental model an agent must hold to operate Codiac correctly. These concepts are compact but
4
+ easy to conflate; getting them straight is what lets you pick the right command and fill its
5
+ arguments with the right *values* (flag names you get from `--help`, but the *meaning* of the
6
+ values comes from here).
7
+
8
+ > Rule of thumb: if you catch yourself unsure what a term means (is a cabinet a cluster? does
9
+ > config live on the asset or the cabinet?), stop and re-read the relevant section. A wrong
10
+ > mental model produces confident, wrong commands.
11
+
12
+ ## Contents
13
+ 1. [The topology: tenant → enterprise → environment → cabinet → cluster](#1-the-topology)
14
+ 2. [Assets: the deployable unit](#2-assets)
15
+ 3. [Scoped configuration and secrets](#3-scoped-configuration-and-secrets)
16
+ 4. [Versioning and the deploy model](#4-versioning-and-the-deploy-model)
17
+ 5. [Cloud/registry auth (CSP)](#5-cloudregistry-auth-csp)
18
+ 6. [Hosts and ingress](#6-hosts-and-ingress)
19
+ 7. [The API surface (NATS, not HTTP)](#7-the-api-surface)
20
+ 8. [Agents on Codiac](#8-agents-on-codiac)
21
+
22
+ ---
23
+
24
+ ## 1. The topology
25
+
26
+ Codiac nests five things. Keep them in this order:
27
+
28
+ ```
29
+ tenant → enterprise → environment → cabinet → cluster
30
+ ```
31
+
32
+ - **Tenant** — the account boundary (e.g. `ben`, `cod`). Everything you do happens inside one
33
+ tenant. A CLI session has a *current tenant*; set it with `cod tenant switch <code>`. A deployed
34
+ agent typically already runs inside its own tenant's context.
35
+ - **Enterprise** — a workspace within a tenant (e.g. `main`). A tenant can have several. There are
36
+ **two enterprise *types***:
37
+ - normal / **`sdlc`** — where your application workloads live.
38
+ - **`infrx`** — infrastructure enterprise, holding cluster-scoped components (operators, agents,
39
+ platform stacks). Some commands are "infrx-only" or "sdlc-only" and will auto-switch or reject
40
+ based on type. If a command complains about enterprise type, you're likely in the wrong one.
41
+ - **Environment** — a stage inside an enterprise (e.g. `dev`, `prod`). **An environment maps to a
42
+ cluster** (or clusters). The mapping is **data-driven in the DB**, not something you pass on the
43
+ command line — which is why you attach clusters to environments (`cod environment cluster attach`),
44
+ not to cabinets.
45
+ - **Cabinet** — the concrete deployment target: a named slot inside an environment where assets get
46
+ deployed. Because a cabinet lives in an environment, it **inherits that environment's cluster**.
47
+ A cabinet corresponds to a Kubernetes namespace (typically `<tenant>-<cabinet>`, e.g. `ben-dev`).
48
+ - **Cluster** — the actual Kubernetes cluster. Cabinets and clusters are *ephemeral over the
49
+ underlying namespace*; the durable link is environment → cluster.
50
+
51
+ **The trap this prevents:** `cod cabinet create` takes **`--environment`**, never `--cluster`.
52
+ You create a cabinet in an environment; the cluster comes along via the env mapping. Trying to
53
+ pass a cluster to `cabinet create` is a classic mistake.
54
+
55
+ Related commands: `cod enterprise list`, `cod environment list/create`,
56
+ `cod environment cluster attach/detach`, `cod cabinet create/list/destroy`,
57
+ `cod cluster connect/list`. (Always `--help` for exact flags — see the CLI reference.)
58
+
59
+ ---
60
+
61
+ ## 2. Assets
62
+
63
+ An **asset** is a deployable workload. Two kinds matter:
64
+
65
+ - **`helm`** — deployed from a Helm chart (chart identity lives on the asset's image ref:
66
+ chart name + registry + version).
67
+ - **`service`** (container) — Codiac builds a Deployment for it from a container image.
68
+
69
+ Lifecycle:
70
+ 1. **Register** the asset once (`cod asset create`) — this records its identity in the enterprise.
71
+ 2. **Version** it — a specific version is captured as an `AssetVersionConfig`.
72
+ 3. **Deploy** a version into a cabinet (`cod asset deploy`). Deploy is idempotent-ish
73
+ (`helm upgrade --install` under the hood for helm assets).
74
+ 4. Inspect/operate: `cod asset list`, `cod asset view` (logs/term/exec/env/config/status),
75
+ `cod asset undeploy`, `cod asset recycle`.
76
+
77
+ **Image registries — policy:** never AWS ECR. Images live in **Docker Hub** or **Azure
78
+ Container Registry (ACR)**. Registries are captured as first-class objects
79
+ (`cod imageRegistry ...`, `cod secretStore ...` for pull secrets).
80
+
81
+ The `type` of an asset (`helm` vs `service`) changes which config `targetFile`s apply (see §3):
82
+ `[helm]` values for charts, `[env]`/`[footprint]`/etc. for services.
83
+
84
+ ---
85
+
86
+ ## 3. Scoped configuration and secrets
87
+
88
+ This is the richest — and most misunderstood — part of Codiac. Configuration is **not** stored
89
+ on the asset as one blob. It is a set of **scoped settings** that get *smash-merged* into a config
90
+ document at deploy time.
91
+
92
+ ### Scope and inheritance
93
+ Every setting has a **scope**, one of:
94
+
95
+ - `enterprise`
96
+ - `environment`
97
+ - `cabinet`
98
+
99
+ **Precedence: cabinet overrides environment overrides enterprise.** A cabinet-scoped value wins
100
+ for that cabinet; otherwise the value is *inherited* from the environment, then the enterprise.
101
+
102
+ **Design principle:** put shareable config at the **highest** scope that makes sense — set it
103
+ *once* at the enterprise, let every cabinet inherit it, and add a narrower (cabinet/environment)
104
+ override only when a specific target genuinely differs. Cabinet-scoping something that should be
105
+ shared is an anti-pattern: it breaks reproducibility (every new cabinet would need it re-set) and
106
+ a stray empty cabinet override silently shadows a good enterprise value.
107
+
108
+ ### targetFile — where a setting lands at runtime
109
+ A setting's `targetFile` says *what artifact it becomes*:
110
+
111
+ | targetFile | Becomes |
112
+ |---|---|
113
+ | `[helm]` | a Helm chart value (`.Values.<path>`) for a helm asset |
114
+ | `[env]` | an OS environment variable on the container |
115
+ | `[footprint]` | container resource footprint (cpu/mem requests/limits) |
116
+ | `[label]` | a Kubernetes label |
117
+ | `[annotation]` | a Kubernetes annotation |
118
+ | `[filestore]` | a mounted file/volume value |
119
+ | a file path (e.g. `app-config.json`) | a key inside that config file |
120
+
121
+ Choosing the wrong `targetFile` is a common error — e.g. putting a helm chart value under `[env]`
122
+ (where Codiac builds Deployments for *service* assets) instead of `[helm]`.
123
+
124
+ ### Literal value vs sourced reference
125
+ A setting's value is either:
126
+ - a **literal** `value` (stored directly), or
127
+ - a **sourced** reference (`KvpReference`) that points into a **vault / KVP store** — the concrete
128
+ value is resolved at deploy time. This keeps secrets out of the config records. Sourcing requires
129
+ a vault store to exist for the tenant.
130
+
131
+ ### Dotted keys and the escaped-period trap
132
+ A setting *key* can be a dotpath — e.g. `dashboard.password` — which **nests** into the config
133
+ document (`{ dashboard: { password: ... } }`) via an object-flattener. Consequences you must
134
+ respect:
135
+ - The dot is a **hierarchy delimiter**. `ingress.host` sets `.Values.ingress.host` in a helm chart.
136
+ - Internally, dotted keys are **stored with escaped periods (`\.`)**. An exact-string match on the
137
+ raw dotted key can silently miss — normalize `\.`→`.` when comparing, and don't assume a naive
138
+ query on a dotted name will match.
139
+ - **Vault key names can't contain dots** (Azure Key Vault allows only `[0-9A-Za-z-]`). When a
140
+ dotted key is sourced into a vault, the vault key must be sanitized (dots→dashes) or it fails to
141
+ round-trip and resolves empty.
142
+
143
+ ### Templates (`@{...}`)
144
+ Setting values can be **view-engine templates** resolved per target. Example ingress host:
145
+ `@{asset.name}.@{cabinet.name}.@{environment.name}.example.com` — one enterprise-scope value that
146
+ resolves to a different concrete host in each cabinet. Prefer a shareable template at enterprise
147
+ scope over a concrete value re-computed per cabinet.
148
+
149
+ ### Commands
150
+ - `cod config view` — **preview the resolved config doc** for a cabinet (all scopes merged,
151
+ sources resolved). Your best "what will actually deploy?" check.
152
+ - `cod config add` (alias `config set`) — set a scoped setting (`--setting`, `--value` /
153
+ `--value-stdin`, scope via `--enterprise-scope`/`--enviro`/`--cabinet`, `--type` = targetFile).
154
+ - `cod config settings get` — inspect the *raw* stored settings (filter by `--setting`/`--value`
155
+ globs). Good for "is this set, and at what scope?".
156
+
157
+ > Flag caution: in `config add` and `config settings get`, **`-n` = enterprise and `-e` =
158
+ > environment (`--enviro`)** — the reverse of the usual `-e = enterprise`. `config view` uses
159
+ > `-e = enterprise`. Always `--help`.
160
+
161
+ ---
162
+
163
+ ## 4. Versioning and the deploy model
164
+
165
+ - **AssetVersionConfig** — a specific captured version of an asset.
166
+ - **EnterpriseVersionConfig (EVC)** — the **versioned snapshot of the enterprise's state** that the
167
+ platform reads (the rootmap the CLI shows is derived from it). A cabinet points at an EVC version.
168
+ - **Deploy** (`cod asset deploy`, or the relay `invokeDeploymentSequence`) records the deployed
169
+ asset+version into the enterprise state. The relay writes EVCs through a `getsertVersion` call and
170
+ creates a **DeployAttempt** record for each deploy.
171
+ - **Snapshots** (`cod snapshot ...`) tag enterprise states so they can be re-deployed/rolled.
172
+
173
+ Why it matters: many downstream systems (status/UI, the asset→label mapping publisher) read the
174
+ **EVC**, not the live cluster. If a freshly-deployed asset isn't reflected where you expect,
175
+ suspect an EVC/snapshot timing issue rather than a cluster problem — re-check with `cod config
176
+ view` / `cod asset list` (both EVC-derived).
177
+
178
+ ---
179
+
180
+ ## 5. Cloud/registry auth (CSP)
181
+
182
+ CSP = Cloud Service Provider integration.
183
+
184
+ - A tenant onboards providers via a **cspTenantProfile** (`cod csp setup`).
185
+ - **`cod csp login <provider>`** authenticates the tenant's own creds for that provider; tokens are
186
+ cached **server-side per tenant**, so a later headless operation (deploy, agent install) can use
187
+ them without re-login.
188
+ - You typically need **two** logins for a full deploy: the **cloud provider** that owns the cluster
189
+ (e.g. `azure`) *and* the **registry** hosting the chart/image (e.g. `docker` for Docker Hub).
190
+ A "no credentials found" / "DockerHub credentials not found" failure during deploy almost always
191
+ means one of these logins is missing.
192
+ - Provider is a **positional arg** on `cod csp login` (`aws`/`azure`/`docker`), and secrets are fed
193
+ via **stdin** in `--silent` mode.
194
+
195
+ Commands like `cod cluster connect <name> --silent` use the cached CSP creds to set up kubectl
196
+ auth — prefer it over raw cloud CLIs for cluster access.
197
+
198
+ ---
199
+
200
+ ## 6. Hosts and ingress
201
+
202
+ - A workload with an ingress needs a **host**. Codiac resolves hosts from a **host-naming strategy**
203
+ attached to a scope (`ScopedHostNameConfig`) — e.g. `svc_dot_cab_dot_domain`
204
+ (`<service>.<cabinet>.<domain>`) or `shared_ingress_ip`.
205
+ - You can set the ingress host directly as a `[helm]` scoped setting (`ingress.host`), ideally as a
206
+ shareable enterprise-scope **template** (`@{asset.name}.@{cabinet.name}.….<domain>`) so it
207
+ resolves per cabinet.
208
+ - Commands: `cod host list/map/view/delete`.
209
+
210
+ ---
211
+
212
+ ## 7. The API surface
213
+
214
+ Codiac's data plane is **NATS, not HTTP**. Key implications:
215
+
216
+ - The platform communicates over **NATS pub/sub** (subjects, request/reply, streaming events), not
217
+ REST. The web frontend follows a **Facade → NATS → Store (Akita) → Query → Component** pattern;
218
+ it does not fetch over HTTP for platform data.
219
+ - There is a **relay** with endpoints (operationIds) invoked over NATS; the CLI's `executeTask`
220
+ makes exactly one such relay call per command. This is why the CLI is the safe surface — it wraps
221
+ the correct subjects, auth, and payloads for you.
222
+ - There is a **tenant → hub account boundary**. A long *synchronous* request/reply does **not**
223
+ survive the import across that boundary — long operations use an **ack-then-stream** pattern
224
+ (return an ack immediately, then publish terminal status as an event on the tenant's
225
+ `events.>` channel). If you ever build against the API directly, don't expect a long blocking
226
+ reply from a customer tenant to the hub; subscribe for the streamed result instead.
227
+
228
+ Practically: **use the CLI.** Only reach for the raw NATS API when the CLI genuinely cannot express
229
+ what you need — and then mirror how the CLI builds the call.
230
+
231
+ ---
232
+
233
+ ## 8. Agents on Codiac
234
+
235
+ Codiac can run AI agents as first-class workloads.
236
+
237
+ - **`cod ai agent install`** provisions an agent into a cabinet. `--type` selects the framework:
238
+ `openclaw`, `hermes`, or `byoa` (bring-your-own-agent). `--name` is the workload name, `-c` the
239
+ cabinet, and ingress is on by default (needs a `--naming` strategy in silent mode).
240
+ - Provisioning follows the same asset/scoped-config/deploy machinery above: it registers an asset,
241
+ writes scoped settings (secrets sourced to the vault, shared config at enterprise scope), maps the
242
+ ingress host, and deploys — so everything in §§2–6 applies to agents too.
243
+ - Secrets an agent needs (API keys, dashboard password) are scoped settings; set them **once** at
244
+ enterprise scope and let cabinets inherit, rather than re-prompting per install.