@cyaxis/ulvia 0.0.2 → 0.1.0-beta

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.
package/INSTALLATION.md CHANGED
@@ -32,16 +32,19 @@ and accessible to the account that executes `ulvia`.
32
32
 
33
33
  ## 2. Install The CLI
34
34
 
35
- Install the current prerelease globally from the public npm registry:
35
+ Install the current stable CLI globally from the public npm registry:
36
36
 
37
37
  ```bash
38
- bun add --global @cyaxis/ulvia@next
38
+ bun add --global @cyaxis/ulvia@latest
39
39
  ulvia --help
40
40
  ```
41
41
 
42
42
  If `ulvia` is not found, reopen the terminal and check that `~/.bun/bin` is in
43
43
  `PATH`, as described by the Bun installation guide.
44
44
 
45
+ Use an exact version, for example `@cyaxis/ulvia@0.0.3`, when reproducing an
46
+ installation. The `next` tag is reserved for explicitly tested prereleases.
47
+
45
48
  ## 3. Create A Local Site
46
49
 
47
50
  Start the shared infrastructure, then create one named instance:
@@ -97,10 +100,13 @@ site unavailable because Kernels depend on the shared Provider and proxy.
97
100
 
98
101
  ## 5. Upgrade Ulvia
99
102
 
100
- Upgrade the host CLI through Bun:
103
+ Upgrade the host CLI through Bun. Naming `latest` explicitly also replaces an
104
+ installation that was originally attached to the `next` channel:
101
105
 
102
106
  ```bash
103
- bun update --global @cyaxis/ulvia
107
+ bun add --global @cyaxis/ulvia@latest
108
+ hash -r
109
+ ulvia --version
104
110
  ```
105
111
 
106
112
  Upgrade deployable components independently with exact versions:
@@ -116,6 +122,14 @@ that digest on subsequent starts. Registry channels such as `next` or `latest`
116
122
  may identify releases for humans, but an installation never persists or
117
123
  silently follows those mutable tags.
118
124
 
125
+ `upgrade kernel` visits every local Kernel instance in sequence. Upgrades of
126
+ Provider and Repository affect shared services. A failed activation attempts
127
+ to restart the previous image, but this does not restore changed database or
128
+ file contents and is not an atomic rollback across all instances.
129
+ Take a complete backup before an upgrade involving persisted data. Internal
130
+ schema migrations and interrupted-upgrade/restore drills remain
131
+ [open platform work](../TODO.md#internal-persistence-migrations).
132
+
119
133
  ## 6. Public Domain And HTTPS
120
134
 
121
135
  Configure public networking before creating the first instance because the
@@ -149,6 +163,12 @@ directory. Preserve the complete directory, including MongoDB, files,
149
163
  credentials, encryption material, Repository state, and instance definitions.
150
164
  Do not restore only selected secret or database files from different snapshots.
151
165
 
166
+ The CLI has no general backup/restore command. Stop each Kernel with
167
+ `ulvia instance stop <name>`, then stop shared services before copying their
168
+ data. Restore and verify a complete backup in an isolated installation before
169
+ treating it as a recovery guarantee; do not rely on image fallback to recover
170
+ data. See [operational recovery](../TODO.md#backup-and-operational-recovery).
171
+
152
172
  `reset` permanently removes Ulvia-owned local state:
153
173
 
154
174
  ```bash
@@ -180,3 +200,134 @@ Removing the CLI alone does not delete persistent CMS data or Docker resources.
180
200
 
181
201
  For implementation diagnostics and contributor-only environment overrides, see
182
202
  the [development and validation guide](../development/README.md).
203
+
204
+
205
+ ## 9. Standalone Repository
206
+
207
+ A public Repository is independent of CMS shared services. It needs Docker and
208
+ the CLI, with one persistent local volume and one active server. The commands
209
+ below describe the candidate implementation; the exact CLI and image versions
210
+ must be published before they can be installed or pulled from a registry.
211
+
212
+ On the Repository host, configure networking and start the default deployment:
213
+
214
+ ```bash
215
+ ulvia repository configure --tls external --public-url https://repository.example
216
+ ulvia repository start --version 0.1.0
217
+ ulvia repository status
218
+ ulvia repository stop
219
+ ulvia repository start
220
+ ulvia repository upgrade --version 0.1.1
221
+ ```
222
+
223
+ The versions above are examples, not a claim that those images are published.
224
+ No `global`, `community` or deployment name is required. The CLI pulls an exact
225
+ official image, validates its labels and pins its immutable registry digest.
226
+ It does not build or publish images. A failed activation attempts to restart the
227
+ previous image; it does not reverse data migrations. Back up data before upgrades.
228
+
229
+ Choose one persistent network mode:
230
+
231
+ | Mode | Configuration | Exposure |
232
+ | --- | --- | --- |
233
+ | Local | `repository configure --tls local --port 5010` | Loopback HTTP only |
234
+ | External proxy | `repository configure --tls external --public-url https://repository.example` | Loopback backend; operator supplies the HTTPS proxy |
235
+ | Managed ACME | `repository configure --tls acme --public-url https://repository.example --acme-email ops@example.com` | Dedicated proxy on host ports 80/443, plus certificate renewals |
236
+
237
+ Run `repository start` after configuring to apply changes. External proxies
238
+ forward to `127.0.0.1:5010` (or the configured port), preserve request bodies and
239
+ allow 100 MiB assets plus protocol overhead. Managed ACME requires public DNS
240
+ pointing to this host and ports 80/443 available; it cannot share these ports
241
+ with another local proxy. It starts only Repository, proxy and ACME containers.
242
+ Certificate issuance requires the public domain; local tests validate the
243
+ Compose model without issuing a public certificate. `REPOSITORY_PUBLIC_URL`
244
+ controls resource links; forwarded headers do not override it.
245
+
246
+ On an author's machine, save the connection once:
247
+
248
+ ```bash
249
+ ulvia repository login https://repository.example
250
+ ulvia identity list
251
+ ulvia namespace claim acme
252
+ ulvia namespace status acme
253
+ ulvia release /path/to/authored-resource
254
+ ulvia push contract acme/example.contract@1.0.0
255
+ ulvia pull contract acme/example.contract@1.0.0
256
+ ```
257
+
258
+ Login creates a default local identity if needed and saves the Repository's
259
+ persistent identity. It does not log into a bearer-token session: every mutation
260
+ is signed locally. Claim is public, first valid claimant wins. The resource's
261
+ `publisherId` must match the owned namespace. Release and push are separate;
262
+ dependencies must be released and pushed first. Collections and provider
263
+ manifests use `push collection` and `push provider` with exact coordinates.
264
+ Publishing an artifact does not install it on a CMS site.
265
+
266
+ Create or import named signing identities when needed:
267
+
268
+ ```bash
269
+ ulvia identity create official
270
+ ulvia identity import imported --key-file /private/existing.pem
271
+ ulvia identity use official
272
+ ulvia identity public-key official
273
+ ulvia repository login https://repository.example --identity official
274
+ ulvia identity create replacement
275
+ ulvia namespace rotate acme --replacement-identity replacement
276
+ ulvia namespace revoke acme --yes
277
+ ulvia repository logout
278
+ ```
279
+
280
+ Rotation requires the current owner's key and proof of the replacement key.
281
+ Publication automatically selects the saved key matching the current namespace
282
+ owner. Revocation is permanent. Losing every copy of an owner key is permanent;
283
+ there is no operator recovery. Logout preserves local identities.
284
+
285
+ The official public Repository will use `https://repo.ulvia.fr`. Its namespace
286
+ `ulvia.official` follows exactly the same first-claim rule. Initialize it on the
287
+ host while the service is still available only over loopback:
288
+
289
+ ```bash
290
+ ulvia repository start --version 0.1.0
291
+ ulvia identity create official
292
+ ulvia repository login http://127.0.0.1:5010 --identity official
293
+ ulvia namespace claim ulvia.official
294
+ ulvia repository configure --tls acme --public-url https://repo.ulvia.fr --acme-email ops@ulvia.fr
295
+ ulvia repository start
296
+ ulvia repository login https://repo.ulvia.fr --identity official
297
+ ulvia release /path/to/official-contract
298
+ ulvia push contract ulvia.official/example.contract@1.0.0
299
+ ```
300
+
301
+ Run the first claim before opening public access. A separate signing machine can
302
+ reach the loopback listener through an SSH tunnel instead. There is no reserved
303
+ namespace bootstrap or operator token. After the first claim, existing ownership
304
+ survives restarts, upgrades, rotations and terminal revocation. Tests use temporary local
305
+ repositories; they never contact or publish to `repo.ulvia.fr`.
306
+
307
+ Client settings and identities live in `$XDG_CONFIG_HOME/ulvia` or
308
+ `~/.config/ulvia`, overridable with `ULVIA_CONFIG_DIR`. `config.json` contains
309
+ public keys, key-file references and the active connection; private PEM files
310
+ live in `keys/`. Files are private (0600), directories 0700. Preserve secure
311
+ backups of keys. Setting only `ULVIA_DATA_DIR` isolates client settings beneath
312
+ `<data>/config`, useful for tests or separate installations.
313
+
314
+ Deployment configuration lives in `<data>/repository-service/config.json`;
315
+ `service.json` records the last applied image and configuration; `data/` holds
316
+ Repository identity, namespace history, releases and upload/replay state.
317
+ Managed TLS material lives in `proxy/`. Back up the complete stopped deployment
318
+ and the matching signing identities. `reset --yes` affects the CMS installation
319
+ and local release cache; it preserves independent Repository deployments and
320
+ client identities. There is no compatibility path for previous Repository states, deployment names
321
+ or token credentials.
322
+
323
+ For a contributor's unpublished candidate, use the local Docker regression
324
+ runner instead of `repository start`, which intentionally pulls a published
325
+ registry image:
326
+
327
+ ```bash
328
+ bun run infra/images/build.ts build --version 0.1.0-repository.0 repository
329
+ ULVIA_REPOSITORY_DOCKER_IMAGE=docker.io/cyaxis/ulvia-official-repository:0.1.0-repository.0 bun test infra/images/official-repository/tests
330
+ ```
331
+
332
+ These tests launch loopback-only containers, exercise the CLI against their local
333
+ endpoints and remove their temporary data. They publish nothing externally.
package/README.md CHANGED
@@ -10,25 +10,27 @@ bundled without workspace dependencies and published publicly as
10
10
  `@cyaxis/ulvia`:
11
11
 
12
12
  ```bash
13
- bun add --global @cyaxis/ulvia@next
13
+ bun add --global @cyaxis/ulvia@latest
14
14
  ulvia --help
15
15
  ```
16
16
 
17
17
  From the workspace, build and publish one exact release with:
18
18
 
19
19
  ```bash
20
- bun run cli:build --version 0.0.1-beta
21
- bun run cli:publish --version 0.0.1-beta
20
+ bun run cli:build --version 0.0.3 --stack-version 0.0.1-beta
21
+ bun run cli:publish --version 0.0.3
22
22
  ```
23
23
 
24
24
  The build creates a checked npm archive and SHA-256 receipt below
25
- `artifacts/cli`. Publishing accepts only that exact pair, targets the public npm
26
- registry, and uses the `next` tag for prereleases or `latest` for stable versions.
27
- It requires npm credentials configured outside the repository.
25
+ `artifacts/cli`. The CLI package version and its compatible initial Docker stack
26
+ version are independent and both are sealed into that receipt. Publishing
27
+ accepts only the exact pair, targets the public npm registry, and uses the
28
+ `next` tag for prereleases or `latest` for stable versions. It requires npm
29
+ credentials configured outside the repository.
28
30
 
29
31
  ```bash
30
- bun run ulvia -- start
31
32
  bun run ulvia -- configure network
33
+ bun run ulvia -- services start --version 0.0.1-beta
32
34
  bun run ulvia -- services status
33
35
  bun run ulvia -- instance create preview --label "Preview site"
34
36
  bun run ulvia -- instance list
@@ -50,6 +52,12 @@ bun run ulvia -- restore collection ulvia.official/ulvia-official@1.0.0 --reposi
50
52
  bun run ulvia -- prune
51
53
  ```
52
54
 
55
+ The stack version above is an example of an exact published image tag. Workspace
56
+ source has no embedded initial stack version: its first `services start`
57
+ requires `--version`. Packaged releases embed that default at build time. Later
58
+ starts reuse installed digests. Building workspace source does not replace
59
+ running images or upgrade installed collections.
60
+
53
61
  The lifecycle commands manage the same Docker Compose projects for loopback and
54
62
  public installations from any directory. Persistent
55
63
  data lives below `$XDG_DATA_HOME/ulvia` or `~/.local/share/ulvia`;
@@ -102,6 +110,31 @@ previously created local instance; it never creates an implicit instance.
102
110
 
103
111
  The lifecycle boundary is deliberate:
104
112
 
113
+ Independent Repository deployments use `ulvia repository start`, `status`, `stop`
114
+ and `upgrade --version <exact-version>`. Their default volume and image pin live
115
+ below `<data>/repository-service`. No deployment name or `global`/`community`
116
+ qualifier is required. The command does not start
117
+ shared services or a Kernel.
118
+
119
+ `ulvia repository login <url>` verifies and saves the remote identity and creates
120
+ a default local signing identity if none exists. `ulvia identity create`, `import`,
121
+ `use`, `list` and `public-key` manage reusable Ed25519 identities. Namespace
122
+ commands and publication use the saved connection and select an identity matching
123
+ the namespace's current owner. No environment exports are needed. `logout`
124
+ removes only the connection, preserving keys.
125
+
126
+ Client configuration lives in `$XDG_CONFIG_HOME/ulvia/config.json` or
127
+ `~/.config/ulvia/config.json`; private keys live in its `keys/` directory. Files
128
+ use mode 0600, directories 0700. `ULVIA_CONFIG_DIR` overrides that location;
129
+ `ULVIA_DATA_DIR` alone isolates client configuration under `<data>/config`.
130
+ Deployment configuration is separate: `<data>/repository-service/config.json`.
131
+ `repository configure` persists local, external HTTPS proxy or managed ACME
132
+ networking. All namespaces, including `ulvia.official`, use public first claim.
133
+ Claim platform-owned names locally before enabling public access. Ownership
134
+ persists across restarts and cannot be reset through deployment configuration.
135
+ See the [installation guide](../../../docs/installation/README.md#9-standalone-repository)
136
+ and [Repository architecture](../../../docs/architecture/community-repository.md).
137
+
105
138
  | Command | Provider tenant | Kernel container |
106
139
  | --- | --- | --- |
107
140
  | `services start` | Does not create one | Does not start one |
@@ -110,14 +143,14 @@ The lifecycle boundary is deliberate:
110
143
  | `start` | Never provisions | Starts every existing Kernel after shared services |
111
144
 
112
145
  `instance credentials <name>` prints only that administrator's email and
113
- password. Repository, Provider operator and instance bearer tokens remain in
146
+ password. Provider operator and instance bearer tokens remain in
114
147
  private runtime files. Shared services and every instance have independent
115
148
  `stop`, `status`, and `logs` boundaries. Software upgrades are explicit and
116
149
  component-scoped through `upgrade <component> --version <exact-version>`.
117
150
  Normal starts always reuse the installed digest and never follow a newer tag.
118
151
  `reset --yes`
119
- removes every Compose project, instance, shared state and repository
120
- release. It does not inspect or modify the current working directory.
152
+ removes shared Compose projects, instances, shared state and local repository
153
+ releases. It preserves independent Repository deployments and signing identities. It does not inspect or modify the current working directory.
121
154
  `release <resource-directory>` reads `definition.json` and routes by `kind`:
122
155
  `collection`, `contract`, or `provider-manifest`. It validates and stores one
123
156
  immutable release. A provider manifest must reference exact contract releases
@@ -154,10 +187,10 @@ a disposable provider environment.
154
187
  `push` publishes one exact local coordinate, and `pull` downloads and re-admits
155
188
  one exact remote coordinate before storing it locally. Both support
156
189
  `collection`, `contract`, and `provider`; dependencies must be transferred
157
- first. `ULVIA_REPOSITORY_URL` can replace `--repository`.
158
- Repository writes require `ULVIA_REPOSITORY_TOKEN`; the token is never accepted
159
- as a command-line argument. Every mutation carries a timestamp, one-time nonce,
160
- body digest, and HMAC-SHA-256 signature. The server rejects stale or replayed
190
+ first. `repository login` saves the default endpoint; `--repository` and
191
+ `ULVIA_REPOSITORY_URL` remain explicit overrides. Owner signatures bind Repository
192
+ identity, namespace revision, action, content digest, timestamp and nonce.
193
+ The server rejects stale or replayed
161
194
  requests, serializes writes across processes, re-runs admission and evolution
162
195
  rules, and stages every declared asset as a separately signed raw stream. A final
163
196
  idempotent commit makes the immutable artifact visible only after all byte lengths
@@ -166,9 +199,10 @@ release. An exact republish is idempotent; different bytes at an existing coordi
166
199
  reject.
167
200
 
168
201
  The protocol, exact-coordinate client and mutation endpoint live in
169
- `@bernouy/cms-repository/repository/publication`; the CLI only composes them with
170
- the reference filesystem adapter and its loopback listener. The filesystem
171
- composition persists upload sessions and replay claims below the repository root.
202
+ `@bernouy/cms-repository/repository/publication`. The CLI uses the publication
203
+ client and local filesystem release storage; `official-repository-server`
204
+ composes the HTTP listener inside its container. The filesystem composition
205
+ persists upload sessions and replay claims below the repository root.
172
206
  A production repository can replace those ports without depending on this
173
207
  executable package.
174
208
 
@@ -176,9 +210,7 @@ executable package.
176
210
  resolution without deleting its exact historical bytes. `restore` exposes it
177
211
  again. Contract yanks block new dependent publications while previously
178
212
  published provider manifests remain readable. The loopback Repository uses the
179
- same write protocol and keeps its generated repository token in the private
180
- runtime directory. The removed integration repository/Supabase bridge is not
181
- run.
213
+ same owner-signing protocol. Signing keys stay in local identities.
182
214
 
183
215
  A collection keeps immutable administration copy in recursive locale
184
216
  directories such as `translations/en/collection.json` and
@@ -222,19 +254,20 @@ and do not affect release identity.
222
254
 
223
255
  The Official Repository image contains the admitted CMS contracts, provider
224
256
  manifest, and administration collection. It installs that snapshot only into
225
- empty persistent storage. To publish the same resources manually, release the
226
- CMS contracts before the manifest:
257
+ absent or empty persistent storage. A non-empty repository is never overlaid on
258
+ image upgrade. The workspace bootstrap command builds the admitted snapshot
259
+ used by the image:
227
260
 
228
261
  ```bash
229
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.access
230
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.installations
231
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.content
232
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.files
233
- bun run ulvia -- release packages/official-repository/contracts/ulvia.provider.cms-instances
234
- bun run ulvia -- release packages/official-repository/providers/ulvia.official
235
- bun run ulvia -- start
262
+ bun run generate:official-bootstrap
236
263
  ```
237
264
 
265
+ That command neither publishes to a running repository nor upgrades an existing
266
+ instance. For individual manual releases, use `release` in dependency order:
267
+ exact contract dependencies before dependent contracts and provider manifests.
268
+ Publication, provider profile transitions, collection upgrades and container
269
+ image replacement remain separate actions.
270
+
238
271
  `instance create` provisions the named instance through the Provider operator
239
272
  API, then starts its Kernel. Connection review validates the runtime report and
240
273
  exact manifest claims; it does not yet execute a live conformance suite.
@@ -0,0 +1,38 @@
1
+ services:
2
+ repository:
3
+ environment:
4
+ VIRTUAL_HOST: ${ULVIA_REPOSITORY_DOMAIN}
5
+ VIRTUAL_PORT: "3000"
6
+ LETSENCRYPT_HOST: ${ULVIA_REPOSITORY_DOMAIN}
7
+ proxy:
8
+ image: nginxproxy/nginx-proxy:1.11.5@sha256:e06db5fb9586234e9c2bdef707c8b8d784caa4fb49fbe7285eb3730a501c3eaa
9
+ restart: unless-stopped
10
+ ports:
11
+ - "0.0.0.0:80:80"
12
+ - "0.0.0.0:443:443"
13
+ volumes:
14
+ - /var/run/docker.sock:/tmp/docker.sock:ro
15
+ - ./nginx/client_max_body.conf:/etc/nginx/conf.d/zzz_client_max_body.conf:ro
16
+ - ./nginx/query_safe_access_log.conf:/etc/nginx/conf.d/zzz_query_safe_access_log.conf:ro
17
+ - ${ULVIA_REPOSITORY_PROXY_DIR}/certificates:/etc/nginx/certs
18
+ - ${ULVIA_REPOSITORY_PROXY_DIR}/virtual-hosts:/etc/nginx/vhost.d
19
+ - ${ULVIA_REPOSITORY_PROXY_DIR}/challenge:/usr/share/nginx/html
20
+ healthcheck:
21
+ test: ["CMD", "nginx", "-t"]
22
+ interval: 30s
23
+ timeout: 5s
24
+ retries: 3
25
+ start_period: 5s
26
+ acme:
27
+ image: nginxproxy/acme-companion:2.8.1@sha256:ccfceb9798b9f6586d47eeeadbb8e185c8897b34e011a2739a9b82d5e92a8b6c
28
+ restart: unless-stopped
29
+ depends_on:
30
+ proxy:
31
+ condition: service_healthy
32
+ volumes_from:
33
+ - proxy
34
+ volumes:
35
+ - /var/run/docker.sock:/var/run/docker.sock:ro
36
+ - ${ULVIA_REPOSITORY_PROXY_DIR}/acme:/etc/acme.sh
37
+ environment:
38
+ DEFAULT_EMAIL: ${ULVIA_ACME_EMAIL}
@@ -0,0 +1,32 @@
1
+ services:
2
+ repository:
3
+ image: ${OFFICIAL_REPOSITORY_IMAGE}
4
+ restart: unless-stopped
5
+ init: true
6
+ user: "${ULVIA_UID}:${ULVIA_GID}"
7
+ environment:
8
+ MODE: PROD
9
+ NODE_ENV: production
10
+ REPOSITORY_HOST: 0.0.0.0
11
+ REPOSITORY_PORT: "3000"
12
+ REPOSITORY_PUBLIC_URL: ${ULVIA_REPOSITORY_PUBLIC_URL}
13
+ ULVIA_REPOSITORY_DIR: /var/lib/ulvia-repository
14
+ ULVIA_REPOSITORY_SEED_DIR: ""
15
+ volumes:
16
+ - type: bind
17
+ source: ${ULVIA_REPOSITORY_DIR}
18
+ target: /var/lib/ulvia-repository
19
+ ports:
20
+ - "127.0.0.1:${ULVIA_REPOSITORY_PORT}:3000"
21
+ read_only: true
22
+ tmpfs:
23
+ - /tmp:rw,nosuid,nodev,noexec,size=64m
24
+ cap_drop: [ALL]
25
+ security_opt: [no-new-privileges:true]
26
+ stop_grace_period: 30s
27
+ healthcheck:
28
+ test: ["CMD", "bun", "-e", "const r=await fetch('http://127.0.0.1:3000/readyz'); if(!r.ok) process.exit(1); await r.body?.cancel()"]
29
+ interval: 30s
30
+ timeout: 5s
31
+ retries: 3
32
+ start_period: 5s
@@ -59,10 +59,11 @@ services:
59
59
  NODE_ENV: production
60
60
  REPOSITORY_HOST: 0.0.0.0
61
61
  REPOSITORY_PORT: "3000"
62
+ REPOSITORY_PUBLIC_URL: http://127.0.0.1:${ULVIA_REPOSITORY_PORT}
62
63
  ULVIA_LOG_FILE: /var/log/ulvia/repository.ndjson
63
64
  ULVIA_REPOSITORY_DIR: /var/lib/ulvia-repository
64
65
  ULVIA_REPOSITORY_SEED_DIR: /opt/ulvia-repository-seed
65
- ULVIA_REPOSITORY_TOKEN: ${ULVIA_REPOSITORY_TOKEN}
66
+ stop_grace_period: 30s
66
67
  volumes:
67
68
  - type: bind
68
69
  source: ${ULVIA_REPOSITORY_DIR}