Repository navigation
Expand file tree
/
Copy pathscope-model.qmd
More file actions
268 lines (199 loc) · 9.67 KB
/
Copy pathscope-model.qmd
File metadata and controls
268 lines (199 loc) · 9.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
---
title: "Scope Model Reference"
description: "Namespace and Collection -- complete reference"
---
## Overview
Appbay has two organizational concepts. Each controls a different aspect of how apps are
grouped, configured, and deployed.
| Concept | Controls | Compose mapping | Cardinality |
|---|---|---|---|
| **Namespace** | Deployment identity and variable scope | Container, network and edge-fragment names | Single per app |
| **Collection** | App selection | Which `docker compose up` calls are made | Multi per app |
Collection is NOT part of variable resolution — it is a selector only.
::: {.callout-important}
## `project:` and `environment:` were replaced by `namespace:`
RFC-001 §4 collapsed the two scope fields into one. As of `v0.0.1-alpha.12`:
```yaml
# check-docs-manifests: ignore
# appbay.yaml -- before
project: homelab
environment: prod
# appbay.yaml -- now
namespace: homelab.prod
```
A namespace is **flat and dot-delimited**. Values shared across namespaces live one level up
in the installation config, not in a hierarchy of names.
**A non-default `project:` or `environment:` is now a parse ERROR**, not a silently ignored
key — the manifest fails with a message naming `namespace:`. The literal value `default` is
still accepted, because it carried no information and every shipped manifest used it.
⚠️ `environment:` *inside a service* is Docker Compose's own field and is untouched. Only the
top-level scope field is gone.
:::
::: {.callout-warning}
## One value scope: `${{ns:KEY}}`
| level | what it is | file | a manifest reaches it by |
|---|---|---|---|
| **system** | physical: this installation's runtime, socket, store, home, base domain | `etc/system.yaml` | nothing; it is not a value scope |
| **project** | intent: the apps that run together | `project:` in the manifest; `etc/projects.yaml` for order | `when:` and the start order, not values |
| **namespace** | one deployment of a project: identity and values | `namespace:` in the manifest or `--namespace`; `etc/namespaces/<ns>.yaml` over `default.yaml` | `${{ns:KEY}}` |
| **collection**, **tags** | labels | `collection:`, `tags:` | selection only |
`etc/namespaces/default.yaml` is seeded by `appbay init` with `DOMAIN`; every namespace
inherits it and `<ns>.yaml` overrides per key. The pre-S43 spelling `${{project.DOMAIN}}`
(dotted) resolves as `${{ns:DOMAIN}}` for one release with a warning naming the new form.
Any other scope name is a compile error naming the one scope.
## Namespace
A namespace is one deployment of a project: identity and values. It distinguishes two
instances of the same app in one installation, and it names the file their `${{ns:KEY}}`
values come from.
```yaml
# appbay.yaml
namespace: uom.sim
```
**Absence means "decided at deploy time".** The field is optional and has no default, which
is what lets a value supplied at compile time take effect — a schema default would shadow it.
### What it controls
- Container names: `appbay.<namespace>.<app>.<service>`
- The app's private network: `<namespace>_<app>_internal`
- The shared-network alias other apps dial: `<namespace>_<app>_<service>`
- The generated edge fragment: `<namespace>.<app>.caddy` / `.yml`
- The default ingress host: `<namespace>.<app>.<domain>` when the manifest omits `host:`
- The values file: `etc/namespaces/<namespace>.yaml`
⚠️ **Dots become hyphens wherever the namespace reaches a hostname.** A dot separates labels
in DNS, so `uom.sim_litellm_web` would parse as host `uom` in domain `sim_litellm_web` and
resolve to nothing. An app in namespace `uom.sim` publishes `uom-sim_litellm_web`.
### An un-namespaced app is unchanged
When `namespace:` is absent — the case for every shipped manifest — the namespace is omitted
from all four names entirely:
| | no namespace | `namespace: uom.sim` |
|---|---|---|
| container | `appbay.litellm.web` | `appbay.uom-sim.litellm.web` |
| network | `litellm_internal` | `uom-sim_litellm_internal` |
| alias | `litellm_web` | `uom-sim_litellm_web` |
| edge fragment | `litellm.caddy` | `uom-sim.litellm.caddy` |
This is deliberate: including it unconditionally would rename every container on every
existing host in order to disambiguate installations that have nothing to disambiguate.
### When to use a namespace
- Two instances of one app in one installation (`uom.sim` and `uom.prod`)
- Separate variable values for the same config keys
## Project, collection, tags
A project is the unit of composition (`project: ai-stack`, one per app; `default` when absent); it is what `when:` sees and what `etc/projects.yaml` orders. Collections and tags are labels: `appbay up --collection ai` selects by them and nothing else reads them.
```yaml
# appbay.yaml
collection: [ai-stack, gpu-apps]
```
### What it controls
Which apps are included in a batch operation. Nothing else.
### What it does NOT control
- Variable values (use environment for that)
- Naming prefixes (use project for that)
- Network isolation (use project for that)
### Multi-valued
An app can belong to multiple collections:
```yaml
# ollama
collection: [ai-stack, gpu-apps]
# jellyfin
collection: [media, gpu-apps]
```
### CLI usage
```bash
appbay up --collection ai-stack # Deploy all ai-stack apps
appbay up ollama webui # Deploy specific apps (no collection needed)
appbay down ollama webui tts # Stop apps by name
appbay restart ollama jellyfin # Restart apps by name
```
## Variable resolution
One scope, `${{ns:KEY}}`, read from the deployment's namespace values: `etc/namespaces/<ns>.yaml`
layered over `etc/namespaces/default.yaml`. `appbay init` seeds `default.yaml` with `DOMAIN`, so
every namespace inherits the system's base domain and may override it. There is no cascading
fallthrough to any other store, and an undefined key is a compile error naming the scope and
the key; Appbay never substitutes an empty string. Collections and tags are labels and play no
part in resolution.
```yaml
# etc/namespaces/default.yaml (seeded by init)
DOMAIN: home.lan
TZ: UTC
# etc/namespaces/uom.prod.yaml
DOMAIN: homelab.example.com # this deployment's domain
TZ: America/New_York
```
An app with `namespace: uom.prod` resolves `${{ns:DOMAIN}}` to `homelab.example.com`; an app
with no namespace resolves it to `home.lan`. `--namespace uom.prod` on `up`, `compile` or
`apply` puts every app that pins no namespace into `uom.prod` for that run.
### Magic generators
| Generator | Output | Persisted? |
|---|---|---|
| `${password:N}` | Random N-character password | Yes -- stored in `generated-values.yaml` |
| `${uuid}` | Random UUID v4 | Yes |
| `${base64:N}` | Random N-byte base64 string | Yes |
| `${hash}` | Deterministic hash of (namespace + service + key) | No -- always reproducible |
| `${timestamp}` | Current ISO timestamp | No -- evaluated at render time |
Generated values are keyed by `(namespace, service, var_name)`. Once generated, they are reused on subsequent compiles to ensure deterministic output.
## Compose runtime mapping
When you run `appbay up --collection ai-stack`:
```
1. Select apps tagged with collection: ai-stack
-> [ollama, webui, tts]
2. For each app, resolve variables:
${{ns:KEY}} from $APPBAY_HOME/etc/namespaces/<namespace>.yaml over default.yaml
3. Compile each app:
upstream transform -> overlays -> traits -> render
4. Execute each app as a separate Compose project:
docker compose -f renders/ollama/docker-compose.rendered.yml up -d
docker compose -f renders/webui/docker-compose.rendered.yml up -d
docker compose -f renders/tts/docker-compose.rendered.yml up -d
```
Each app is an independent Compose project. They communicate via shared networks (e.g., `appbay_shared`), not via Compose's internal service discovery.
## Filesystem layout
⚠️ **This is the intended layout, not the built one.** `etc/projects/` is not read by
anything — `ProjectConfigSchema` and `EnvironmentConfigSchema` exist and have no non-test
consumers, and the string `"projects"` is never joined into a path. The one file that is
read is `$APPBAY_HOME/etc/system.yaml` at the root.
```
$APPBAY_HOME/
project.yaml # the ONE config that is read; `domain:` -> ${{ns:DOMAIN}}
etc/
projects/ # ⚠️ specified, not implemented -- nothing reads this
homelab/
project.yaml
environments/
prod.yaml
apps/
ollama/ # App definition
appbay.yaml # namespace: uom.sim, collection: [ai-stack]
docker-compose.yml
jellyfin/
appbay.yaml # collection: [media]
docker-compose.yml
```
## Reserved keys vs tags
| Type | Keys | Semantics |
|---|---|---|
| **Reserved** | `namespace`, `collection`, `operator`, `service` | Fixed meaning -- drives Appbay behavior |
| **Removed** | `project`, `environment` | Replaced by `namespace`; a non-default value is a parse error |
| **Tags** | Anything via `tags:` | No fixed meaning -- UI filtering and bulk ops only |
## Anti-patterns
### Do not use collections for variable scoping
```yaml
# WRONG: no collection-level variable resolution exists
# RIGHT: use a namespace, or app-level overrides
namespace: prod
```
### Do not use a namespace for operational grouping
```yaml
# WRONG -- a namespace is a deployment identity, not a label
namespace: ai-apps
namespace: media-apps
# RIGHT -- group with collections, which are multi-valued
collection: [ai-stack] # ollama
collection: [media] # jellyfin
```
### Do not use a namespace to select different app sets
```yaml
# WRONG -- namespace does not choose which apps deploy
namespace: minimal
namespace: full
# RIGHT -- collections select, the namespace identifies
collection: [core]
collection: [core, extras]
```