@cyaxis/ulvia 0.0.3 → 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,15 +10,15 @@ 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.2 --stack-version 0.0.1-beta
21
- bun run cli:publish --version 0.0.2
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
@@ -29,8 +29,8 @@ accepts only the exact pair, targets the public npm registry, and uses the
29
29
  credentials configured outside the repository.
30
30
 
31
31
  ```bash
32
- bun run ulvia -- start
33
32
  bun run ulvia -- configure network
33
+ bun run ulvia -- services start --version 0.0.1-beta
34
34
  bun run ulvia -- services status
35
35
  bun run ulvia -- instance create preview --label "Preview site"
36
36
  bun run ulvia -- instance list
@@ -52,6 +52,12 @@ bun run ulvia -- restore collection ulvia.official/ulvia-official@1.0.0 --reposi
52
52
  bun run ulvia -- prune
53
53
  ```
54
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
+
55
61
  The lifecycle commands manage the same Docker Compose projects for loopback and
56
62
  public installations from any directory. Persistent
57
63
  data lives below `$XDG_DATA_HOME/ulvia` or `~/.local/share/ulvia`;
@@ -104,6 +110,31 @@ previously created local instance; it never creates an implicit instance.
104
110
 
105
111
  The lifecycle boundary is deliberate:
106
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
+
107
138
  | Command | Provider tenant | Kernel container |
108
139
  | --- | --- | --- |
109
140
  | `services start` | Does not create one | Does not start one |
@@ -112,14 +143,14 @@ The lifecycle boundary is deliberate:
112
143
  | `start` | Never provisions | Starts every existing Kernel after shared services |
113
144
 
114
145
  `instance credentials <name>` prints only that administrator's email and
115
- password. Repository, Provider operator and instance bearer tokens remain in
146
+ password. Provider operator and instance bearer tokens remain in
116
147
  private runtime files. Shared services and every instance have independent
117
148
  `stop`, `status`, and `logs` boundaries. Software upgrades are explicit and
118
149
  component-scoped through `upgrade <component> --version <exact-version>`.
119
150
  Normal starts always reuse the installed digest and never follow a newer tag.
120
151
  `reset --yes`
121
- removes every Compose project, instance, shared state and repository
122
- 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.
123
154
  `release <resource-directory>` reads `definition.json` and routes by `kind`:
124
155
  `collection`, `contract`, or `provider-manifest`. It validates and stores one
125
156
  immutable release. A provider manifest must reference exact contract releases
@@ -156,10 +187,10 @@ a disposable provider environment.
156
187
  `push` publishes one exact local coordinate, and `pull` downloads and re-admits
157
188
  one exact remote coordinate before storing it locally. Both support
158
189
  `collection`, `contract`, and `provider`; dependencies must be transferred
159
- first. `ULVIA_REPOSITORY_URL` can replace `--repository`.
160
- Repository writes require `ULVIA_REPOSITORY_TOKEN`; the token is never accepted
161
- as a command-line argument. Every mutation carries a timestamp, one-time nonce,
162
- 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
163
194
  requests, serializes writes across processes, re-runs admission and evolution
164
195
  rules, and stages every declared asset as a separately signed raw stream. A final
165
196
  idempotent commit makes the immutable artifact visible only after all byte lengths
@@ -168,9 +199,10 @@ release. An exact republish is idempotent; different bytes at an existing coordi
168
199
  reject.
169
200
 
170
201
  The protocol, exact-coordinate client and mutation endpoint live in
171
- `@bernouy/cms-repository/repository/publication`; the CLI only composes them with
172
- the reference filesystem adapter and its loopback listener. The filesystem
173
- 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.
174
206
  A production repository can replace those ports without depending on this
175
207
  executable package.
176
208
 
@@ -178,9 +210,7 @@ executable package.
178
210
  resolution without deleting its exact historical bytes. `restore` exposes it
179
211
  again. Contract yanks block new dependent publications while previously
180
212
  published provider manifests remain readable. The loopback Repository uses the
181
- same write protocol and keeps its generated repository token in the private
182
- runtime directory. The removed integration repository/Supabase bridge is not
183
- run.
213
+ same owner-signing protocol. Signing keys stay in local identities.
184
214
 
185
215
  A collection keeps immutable administration copy in recursive locale
186
216
  directories such as `translations/en/collection.json` and
@@ -224,19 +254,20 @@ and do not affect release identity.
224
254
 
225
255
  The Official Repository image contains the admitted CMS contracts, provider
226
256
  manifest, and administration collection. It installs that snapshot only into
227
- empty persistent storage. To publish the same resources manually, release the
228
- 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:
229
260
 
230
261
  ```bash
231
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.access
232
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.installations
233
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.content
234
- bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.files
235
- bun run ulvia -- release packages/official-repository/contracts/ulvia.provider.cms-instances
236
- bun run ulvia -- release packages/official-repository/providers/ulvia.official
237
- bun run ulvia -- start
262
+ bun run generate:official-bootstrap
238
263
  ```
239
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
+
240
271
  `instance create` provisions the named instance through the Provider operator
241
272
  API, then starts its Kernel. Connection review validates the runtime report and
242
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}