@cyaxis/ulvia 0.0.0-stage → 0.0.2
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 +182 -0
- package/LICENSE +21 -0
- package/README.md +239 -2
- package/dist/definitions/kernel-tls.yml +5 -0
- package/dist/definitions/kernel.yml +40 -0
- package/dist/definitions/mongo/provision-provider.js +21 -0
- package/dist/definitions/nginx/capability_cache.conf +13 -0
- package/dist/definitions/nginx/client_max_body.conf +2 -0
- package/dist/definitions/nginx/default_location +8 -0
- package/dist/definitions/nginx/query_safe_access_log.conf +4 -0
- package/dist/definitions/services-tls.yml +20 -0
- package/dist/definitions/services.yml +177 -0
- package/dist/index.js +29660 -0
- package/package.json +25 -5
package/INSTALLATION.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Install Ulvia
|
|
2
|
+
|
|
3
|
+
This guide installs the Ulvia CLI and creates a first CMS instance on Ubuntu.
|
|
4
|
+
It is for operators and site owners; workspace contributors should use the
|
|
5
|
+
[development guide](../development/README.md).
|
|
6
|
+
|
|
7
|
+
> **Prerelease availability:** `@cyaxis/ulvia` is the official release package.
|
|
8
|
+
> Prereleases use the npm `next` tag; stable releases use `latest`.
|
|
9
|
+
|
|
10
|
+
## 1. Prerequisites On Ubuntu
|
|
11
|
+
|
|
12
|
+
Ulvia requires a supported Ubuntu host, Docker Engine with the Docker Compose
|
|
13
|
+
plugin, and Bun. Follow the upstream instructions rather than copying an
|
|
14
|
+
unversioned third-party installation script:
|
|
15
|
+
|
|
16
|
+
- [Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
|
|
17
|
+
- [Install the Docker Compose plugin on Linux](https://docs.docker.com/compose/install/linux/)
|
|
18
|
+
- [Run Docker without `sudo` and configure startup](https://docs.docker.com/engine/install/linux-postinstall/)
|
|
19
|
+
- [Install Bun on Linux](https://bun.com/docs/installation)
|
|
20
|
+
|
|
21
|
+
Docker's Ubuntu packages include the Compose plugin when
|
|
22
|
+
`docker-compose-plugin` is installed. Verify the complete toolchain:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
docker version
|
|
26
|
+
docker compose version
|
|
27
|
+
bun --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The current release toolchain targets Bun 1.4.2 or newer. Docker must be running
|
|
31
|
+
and accessible to the account that executes `ulvia`.
|
|
32
|
+
|
|
33
|
+
## 2. Install The CLI
|
|
34
|
+
|
|
35
|
+
Install the current prerelease globally from the public npm registry:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bun add --global @cyaxis/ulvia@next
|
|
39
|
+
ulvia --help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If `ulvia` is not found, reopen the terminal and check that `~/.bun/bin` is in
|
|
43
|
+
`PATH`, as described by the Bun installation guide.
|
|
44
|
+
|
|
45
|
+
## 3. Create A Local Site
|
|
46
|
+
|
|
47
|
+
Start the shared infrastructure, then create one named instance:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ulvia services start
|
|
51
|
+
ulvia instance create my-site --label "My site"
|
|
52
|
+
ulvia instance credentials my-site
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The first start uses the exact software version bundled into the installed CLI.
|
|
56
|
+
To bootstrap a different exact prerelease explicitly, use
|
|
57
|
+
`ulvia services start --version 0.0.1-beta`. This option never upgrades an
|
|
58
|
+
existing installation.
|
|
59
|
+
|
|
60
|
+
The site is available at `http://my-site.localhost:5100`. The credentials
|
|
61
|
+
command prints the initial administrator email and password. It never prints
|
|
62
|
+
the Provider, Repository, encryption, or instance service secrets.
|
|
63
|
+
|
|
64
|
+
Ulvia deliberately separates shared services from sites:
|
|
65
|
+
|
|
66
|
+
| Command | Responsibility |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `ulvia services start` | Starts the proxy, MongoDB, Official Repository, and Provider. |
|
|
69
|
+
| `ulvia instance create <name>` | Provisions one Provider tenant and starts its Kernel. |
|
|
70
|
+
| `ulvia instance start <name>` | Restarts an existing instance without provisioning it again. |
|
|
71
|
+
| `ulvia start` | Starts shared services and every existing instance; it creates nothing. |
|
|
72
|
+
|
|
73
|
+
List and inspect the installation at any time:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ulvia services status
|
|
77
|
+
ulvia instance list
|
|
78
|
+
ulvia instance status my-site
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Persistent data lives below `$XDG_DATA_HOME/ulvia` or
|
|
82
|
+
`~/.local/share/ulvia`. Commands can be run from any working directory.
|
|
83
|
+
|
|
84
|
+
## 4. Daily Operations
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
ulvia start
|
|
88
|
+
ulvia instance stop my-site
|
|
89
|
+
ulvia instance start my-site
|
|
90
|
+
ulvia services logs all --level warn
|
|
91
|
+
ulvia instance logs my-site kernel
|
|
92
|
+
ulvia services stop
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Stopping containers preserves persistent data. `services stop` also makes every
|
|
96
|
+
site unavailable because Kernels depend on the shared Provider and proxy.
|
|
97
|
+
|
|
98
|
+
## 5. Upgrade Ulvia
|
|
99
|
+
|
|
100
|
+
Upgrade the host CLI through Bun:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
bun update --global @cyaxis/ulvia
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Upgrade deployable components independently with exact versions:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
ulvia upgrade kernel --version 0.0.1-beta
|
|
110
|
+
ulvia upgrade provider --version 0.0.1-beta
|
|
111
|
+
ulvia upgrade repository --version 0.0.1-beta
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Ulvia resolves each official Docker Hub tag to an immutable digest and reuses
|
|
115
|
+
that digest on subsequent starts. Registry channels such as `next` or `latest`
|
|
116
|
+
may identify releases for humans, but an installation never persists or
|
|
117
|
+
silently follows those mutable tags.
|
|
118
|
+
|
|
119
|
+
## 6. Public Domain And HTTPS
|
|
120
|
+
|
|
121
|
+
Configure public networking before creating the first instance because the
|
|
122
|
+
canonical Kernel URLs become part of Provider provisioning:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
ulvia configure network \
|
|
126
|
+
--bind 0.0.0.0 \
|
|
127
|
+
--http-port 80 \
|
|
128
|
+
--tls acme \
|
|
129
|
+
--https-port 443 \
|
|
130
|
+
--acme-email ops@example.com
|
|
131
|
+
|
|
132
|
+
ulvia services start
|
|
133
|
+
ulvia instance create website --domain example.com
|
|
134
|
+
ulvia instance credentials website
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Before starting, point the domain's DNS records to the host and allow inbound
|
|
138
|
+
TCP traffic on ports 80 and 443. Review Docker's documented firewall behavior:
|
|
139
|
+
published container ports can bypass some `ufw` and `firewalld` rules unless
|
|
140
|
+
the host policy uses Docker's supported chains.
|
|
141
|
+
|
|
142
|
+
Repository and Provider management endpoints remain bound to loopback. Only the
|
|
143
|
+
shared proxy is exposed publicly.
|
|
144
|
+
|
|
145
|
+
## 7. Backup, Reset, And Removal
|
|
146
|
+
|
|
147
|
+
Stop the installation before taking a filesystem-level backup of the Ulvia data
|
|
148
|
+
directory. Preserve the complete directory, including MongoDB, files,
|
|
149
|
+
credentials, encryption material, Repository state, and instance definitions.
|
|
150
|
+
Do not restore only selected secret or database files from different snapshots.
|
|
151
|
+
|
|
152
|
+
`reset` permanently removes Ulvia-owned local state:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
ulvia reset --yes
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Remove the CLI separately after stopping or resetting the installation:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
bun remove --global @cyaxis/ulvia
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Removing the CLI alone does not delete persistent CMS data or Docker resources.
|
|
165
|
+
|
|
166
|
+
## 8. Troubleshooting
|
|
167
|
+
|
|
168
|
+
- **Docker permission denied:** complete Docker's Linux post-installation steps,
|
|
169
|
+
then start a new login session.
|
|
170
|
+
- **`docker compose` is unknown:** install the official Compose plugin rather
|
|
171
|
+
than the retired standalone `docker-compose` package.
|
|
172
|
+
- **Port 5100, 80, or 443 is occupied:** inspect the owner before changing the
|
|
173
|
+
persisted network configuration.
|
|
174
|
+
- **An image cannot be pulled:** verify network access and that the requested
|
|
175
|
+
exact Cyaxis image version has been published.
|
|
176
|
+
- **An instance returns 503:** check `ulvia services status`, then the Provider
|
|
177
|
+
and instance Kernel logs.
|
|
178
|
+
- **Stored encrypted data cannot be opened:** do not generate replacement keys
|
|
179
|
+
over an existing data directory; restore the matching complete backup.
|
|
180
|
+
|
|
181
|
+
For implementation diagnostics and contributor-only environment overrides, see
|
|
182
|
+
the [development and validation guide](../development/README.md).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 matthias-bernouy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,240 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Ulvia CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ulvia installation and release CLI.
|
|
4
|
+
|
|
5
|
+
For the operator journey from a fresh Ubuntu host to a running local or public
|
|
6
|
+
site, follow the [canonical installation guide](INSTALLATION.md).
|
|
7
|
+
|
|
8
|
+
The workspace package contains private source code. Official releases are
|
|
9
|
+
bundled without workspace dependencies and published publicly as
|
|
10
|
+
`@cyaxis/ulvia`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bun add --global @cyaxis/ulvia@next
|
|
14
|
+
ulvia --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
From the workspace, build and publish one exact release with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun run cli:build --version 0.0.1-beta
|
|
21
|
+
bun run cli:publish --version 0.0.1-beta
|
|
22
|
+
```
|
|
23
|
+
|
|
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.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun run ulvia -- start
|
|
31
|
+
bun run ulvia -- configure network
|
|
32
|
+
bun run ulvia -- services status
|
|
33
|
+
bun run ulvia -- instance create preview --label "Preview site"
|
|
34
|
+
bun run ulvia -- instance list
|
|
35
|
+
bun run ulvia -- instance credentials preview
|
|
36
|
+
bun run ulvia -- instance stop preview
|
|
37
|
+
bun run ulvia -- instance delete preview --yes
|
|
38
|
+
bun run ulvia -- services stop
|
|
39
|
+
bun run ulvia -- upgrade kernel --version 0.0.1-beta
|
|
40
|
+
bun run ulvia -- upgrade provider --version 0.0.1-beta
|
|
41
|
+
bun run ulvia -- upgrade repository --version 0.0.1-beta
|
|
42
|
+
bun run ulvia -- reset --yes
|
|
43
|
+
bun run ulvia -- release packages/official-repository/collections/ulvia-official
|
|
44
|
+
bun run ulvia -- release /path/to/contract-directory
|
|
45
|
+
bun run ulvia -- release /path/to/provider-directory
|
|
46
|
+
bun run ulvia -- push contract ulvia.official/ulvia.cms.installations@1.0.0 --repository https://repository.example
|
|
47
|
+
bun run ulvia -- pull collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example
|
|
48
|
+
bun run ulvia -- yank collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example --reason "Superseded"
|
|
49
|
+
bun run ulvia -- restore collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example
|
|
50
|
+
bun run ulvia -- prune
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The lifecycle commands manage the same Docker Compose projects for loopback and
|
|
54
|
+
public installations from any directory. Persistent
|
|
55
|
+
data lives below `$XDG_DATA_HOME/ulvia` or `~/.local/share/ulvia`;
|
|
56
|
+
`ULVIA_DATA_DIR` remains an explicit isolation override for tests and CI. The
|
|
57
|
+
CLI stores authored releases in `<data>/repository`. Shared Provider, Repository,
|
|
58
|
+
MongoDB, proxy, files, secrets and logs live below `<data>/services`; each
|
|
59
|
+
stateless Kernel has independent state and logs below `<data>/instances/<name>`.
|
|
60
|
+
The CLI never starts application servers, supervises their PIDs, or builds
|
|
61
|
+
software images. It pulls only the canonical public images under
|
|
62
|
+
`docker.io/cyaxis`, validates their Ulvia component and stack-API labels, and
|
|
63
|
+
stores the immutable repository digest selected for each component.
|
|
64
|
+
|
|
65
|
+
The first lifecycle command creates a safe installation configuration bound to
|
|
66
|
+
`127.0.0.1:5100`. Configure public exposure before creating an instance:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
ulvia configure network \
|
|
70
|
+
--bind 0.0.0.0 \
|
|
71
|
+
--http-port 80 \
|
|
72
|
+
--tls acme \
|
|
73
|
+
--https-port 443 \
|
|
74
|
+
--acme-email ops@example.com
|
|
75
|
+
ulvia start
|
|
76
|
+
ulvia instance create website --domain example.com
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The configuration is persisted under `<data>/services/installation.json` and
|
|
80
|
+
is not a per-start mode. ACME requires `0.0.0.0` and every instance in an ACME
|
|
81
|
+
installation requires an explicit DNS domain. Network configuration is locked
|
|
82
|
+
after the first instance because its canonical Kernel URL is part of Provider
|
|
83
|
+
provisioning. Repository and Provider management ports remain loopback-only.
|
|
84
|
+
|
|
85
|
+
`services start` starts only the shared proxy, MongoDB, Repository and Provider.
|
|
86
|
+
On first initialization it uses the exact version embedded by `cli:build`, or
|
|
87
|
+
an explicit `services start --version <version>` override. Later starts reuse
|
|
88
|
+
the persisted digests and reject a conflicting `--version`; component changes
|
|
89
|
+
must go through `upgrade`.
|
|
90
|
+
`instance create` provisions one isolated Provider tenant and immediately starts
|
|
91
|
+
its dedicated Kernel. `instance start` restarts only an already-provisioned
|
|
92
|
+
instance's Kernel. All Kernels use the shared proxy port: an instance named
|
|
93
|
+
`default` is available at `http://localhost:5100`, while any other instance such
|
|
94
|
+
as `preview` uses `http://preview.localhost:5100`.
|
|
95
|
+
`ULVIA_HTTP_PORT`, `ULVIA_REPOSITORY_PORT`, and `ULVIA_PROVIDER_PORT` provide
|
|
96
|
+
initial isolation defaults for tests and CI. Interactive configuration belongs
|
|
97
|
+
to `ulvia configure network`. The Provider still listens on its canonical port
|
|
98
|
+
5103 inside the private Docker network; only its operator port on the host
|
|
99
|
+
changes.
|
|
100
|
+
`start` is the idempotent convenience command for shared services plus every
|
|
101
|
+
previously created local instance; it never creates an implicit instance.
|
|
102
|
+
|
|
103
|
+
The lifecycle boundary is deliberate:
|
|
104
|
+
|
|
105
|
+
| Command | Provider tenant | Kernel container |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `services start` | Does not create one | Does not start one |
|
|
108
|
+
| `instance create <name>` | Provisions one durable tenant | Creates and starts its Kernel |
|
|
109
|
+
| `instance start <name>` | Reuses the existing tenant | Starts only that Kernel |
|
|
110
|
+
| `start` | Never provisions | Starts every existing Kernel after shared services |
|
|
111
|
+
|
|
112
|
+
`instance credentials <name>` prints only that administrator's email and
|
|
113
|
+
password. Repository, Provider operator and instance bearer tokens remain in
|
|
114
|
+
private runtime files. Shared services and every instance have independent
|
|
115
|
+
`stop`, `status`, and `logs` boundaries. Software upgrades are explicit and
|
|
116
|
+
component-scoped through `upgrade <component> --version <exact-version>`.
|
|
117
|
+
Normal starts always reuse the installed digest and never follow a newer tag.
|
|
118
|
+
`reset --yes`
|
|
119
|
+
removes every Compose project, instance, shared state and repository
|
|
120
|
+
release. It does not inspect or modify the current working directory.
|
|
121
|
+
`release <resource-directory>` reads `definition.json` and routes by `kind`:
|
|
122
|
+
`collection`, `contract`, or `provider-manifest`. It validates and stores one
|
|
123
|
+
immutable release. A provider manifest must reference exact contract releases
|
|
124
|
+
already in the local repository. Release does not connect to or test a provider.
|
|
125
|
+
Contract fixture assets declared in `definition.json` are read from
|
|
126
|
+
`fixtures/<asset-id>` and verified by digest. Releasing the same coordinate with
|
|
127
|
+
different content fails. The local Repository serves stored resources
|
|
128
|
+
at `/v1/collections`, `/v1/contracts`, and `/v1/providers` on loopback port
|
|
129
|
+
5102, with declared contract fixtures at
|
|
130
|
+
`/v1/contracts/<publisher>/<contract>/<version>/fixtures/<asset-id>`, and sees
|
|
131
|
+
new releases without restarting. The CMS consumes the collection and provider
|
|
132
|
+
repository endpoints. The Provider administration Page imports an exact
|
|
133
|
+
manifest, approves an account connection and replaces the site's exact contract
|
|
134
|
+
selections for Gateway calls. `prune` empties all local repository content
|
|
135
|
+
without deleting service or instance data.
|
|
136
|
+
|
|
137
|
+
Contract authors may keep only release metadata in `definition.json` and split
|
|
138
|
+
capabilities recursively below `capabilities/`. Each capability is one JSON
|
|
139
|
+
object. Optional `mocks/**/*.json` fragments contain one mock plus its explicit
|
|
140
|
+
`capabilityId`; mocks embedded in capability fragments reject. The compiler
|
|
141
|
+
sorts capabilities and mocks by their declared IDs, so paths never affect the
|
|
142
|
+
immutable digest. `definition.json` cannot contain an inline `capabilities`
|
|
143
|
+
array: the recursive source tree is the only authored contract format.
|
|
144
|
+
|
|
145
|
+
An optional conformance companion uses `conformance/definition.json`, recursive
|
|
146
|
+
`conformance/scenarios/**/*.json` scenario objects and recursive
|
|
147
|
+
`conformance/exemptions/**/*.json` coverage-exemption objects. Optional suite
|
|
148
|
+
fixture bytes live below `conformance/fixtures/<asset-id>`. The release command
|
|
149
|
+
assembles and admits a present suite, including exact locally released
|
|
150
|
+
dependencies, before storing the contract. Conformance publication and live
|
|
151
|
+
execution are separate Gateway/repository APIs; the CLI does not yet orchestrate
|
|
152
|
+
a disposable provider environment.
|
|
153
|
+
|
|
154
|
+
`push` publishes one exact local coordinate, and `pull` downloads and re-admits
|
|
155
|
+
one exact remote coordinate before storing it locally. Both support
|
|
156
|
+
`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
|
|
161
|
+
requests, serializes writes across processes, re-runs admission and evolution
|
|
162
|
+
rules, and stages every declared asset as a separately signed raw stream. A final
|
|
163
|
+
idempotent commit makes the immutable artifact visible only after all byte lengths
|
|
164
|
+
and SHA-256 digests pass. Interrupted and rejected sessions never expose a partial
|
|
165
|
+
release. An exact republish is idempotent; different bytes at an existing coordinate
|
|
166
|
+
reject.
|
|
167
|
+
|
|
168
|
+
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.
|
|
172
|
+
A production repository can replace those ports without depending on this
|
|
173
|
+
executable package.
|
|
174
|
+
|
|
175
|
+
`yank` is reversible catalogue metadata: it hides a release from new catalogue
|
|
176
|
+
resolution without deleting its exact historical bytes. `restore` exposes it
|
|
177
|
+
again. Contract yanks block new dependent publications while previously
|
|
178
|
+
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.
|
|
182
|
+
|
|
183
|
+
A collection keeps immutable administration copy in recursive locale
|
|
184
|
+
directories such as `translations/en/collection.json` and
|
|
185
|
+
`translations/en/theme/colors.json`. Every JSON object below
|
|
186
|
+
`translations/<locale>/` contributes to that locale's flat catalogue.
|
|
187
|
+
Collection, Bloc, setting, text, theme and Page metadata stores
|
|
188
|
+
reusable keys from those catalogues rather than inline labels. A key may occur
|
|
189
|
+
in only one fragment per locale. The collection locale must define every
|
|
190
|
+
referenced key; additional locale directories may be partial and fall back to
|
|
191
|
+
it.
|
|
192
|
+
|
|
193
|
+
A collection theme is authored as an ordered set of files. Its
|
|
194
|
+
`theme/definition.json` contains the theme label key and the ordered list of
|
|
195
|
+
category IDs. Category files may be organized recursively below `theme/`; the
|
|
196
|
+
scanner finds them by their internal `id`, while the manifest remains the only
|
|
197
|
+
source of display order. A category filename must match its ID, duplicate IDs
|
|
198
|
+
and orphan files reject, and category/token metadata uses the shared translation
|
|
199
|
+
keys. Categories use an `entries` array containing ordinary tokens and optional
|
|
200
|
+
typed `font-face` entries backed by local WOFF2 assets.
|
|
201
|
+
|
|
202
|
+
Collection assets are discovered recursively below `assets/`; their lowercase
|
|
203
|
+
relative paths are their stable IDs. They are not declared in the collection
|
|
204
|
+
`definition.json`. An optional root `assets/definition.json` accepts only a
|
|
205
|
+
`generations` object keyed by those paths. The compiler derives media type,
|
|
206
|
+
size and digest. Safe presentation-only SVG files receive the `inline-svg`
|
|
207
|
+
profile automatically; other SVGs remain downloadable assets but cannot be
|
|
208
|
+
expanded into authored Page DOM.
|
|
209
|
+
|
|
210
|
+
Collection texts use recursive `texts/definitions/**/*.json` arrays for stable
|
|
211
|
+
IDs and administration metadata, plus recursive
|
|
212
|
+
`texts/locales/<locale>/**/*.json` objects for content values. The release
|
|
213
|
+
command joins them into ordinary collection text records. Paths never prefix a
|
|
214
|
+
text ID; duplicate definitions, duplicate per-locale values and unknown value
|
|
215
|
+
IDs reject.
|
|
216
|
+
|
|
217
|
+
Bloc roots are discovered recursively below `blocs/`. A folder becomes a Bloc
|
|
218
|
+
when it contains `definition.json`, its basename must match the declared Bloc
|
|
219
|
+
ID, and discovery stops there so `settings/definition.json` is never mistaken
|
|
220
|
+
for a nested Bloc. Intermediate grouping directories contain only directories
|
|
221
|
+
and do not affect release identity.
|
|
222
|
+
|
|
223
|
+
The Official Repository image contains the admitted CMS contracts, provider
|
|
224
|
+
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:
|
|
227
|
+
|
|
228
|
+
```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
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`instance create` provisions the named instance through the Provider operator
|
|
239
|
+
API, then starts its Kernel. Connection review validates the runtime report and
|
|
240
|
+
exact manifest claims; it does not yet execute a live conformance suite.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
services:
|
|
2
|
+
kernel:
|
|
3
|
+
image: ${KERNEL_IMAGE}
|
|
4
|
+
restart: unless-stopped
|
|
5
|
+
init: true
|
|
6
|
+
user: "${ULVIA_UID}:${ULVIA_GID}"
|
|
7
|
+
environment:
|
|
8
|
+
MODE: PROD
|
|
9
|
+
NODE_ENV: production
|
|
10
|
+
KERNEL_HOST: 0.0.0.0
|
|
11
|
+
KERNEL_PORT: "3000"
|
|
12
|
+
KERNEL_PUBLIC_URL: ${ULVIA_INSTANCE_URL}
|
|
13
|
+
CMS_INSTALLATIONS_PROVIDER_URL: http://ulvia-provider:5103
|
|
14
|
+
CMS_INSTALLATIONS_PROVIDER_TOKEN: ${CMS_INSTALLATIONS_PROVIDER_TOKEN}
|
|
15
|
+
ULVIA_LOG_FILE: /var/log/ulvia/kernel.ndjson
|
|
16
|
+
VIRTUAL_HOST: ${ULVIA_INSTANCE_HOSTNAME}
|
|
17
|
+
VIRTUAL_PORT: "3000"
|
|
18
|
+
volumes:
|
|
19
|
+
- type: bind
|
|
20
|
+
source: ${ULVIA_LOGS_DIR}
|
|
21
|
+
target: /var/log/ulvia
|
|
22
|
+
networks:
|
|
23
|
+
- instances
|
|
24
|
+
read_only: true
|
|
25
|
+
tmpfs:
|
|
26
|
+
- /tmp:rw,nosuid,nodev,noexec,size=256m
|
|
27
|
+
cap_drop: [ALL]
|
|
28
|
+
security_opt: [no-new-privileges:true]
|
|
29
|
+
healthcheck:
|
|
30
|
+
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()"]
|
|
31
|
+
interval: 30s
|
|
32
|
+
timeout: 5s
|
|
33
|
+
retries: 3
|
|
34
|
+
start_period: 10s
|
|
35
|
+
stop_grace_period: 30s
|
|
36
|
+
|
|
37
|
+
networks:
|
|
38
|
+
instances:
|
|
39
|
+
external: true
|
|
40
|
+
name: ${ULVIA_INSTANCE_NETWORK}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const environment = { ...process.env };
|
|
2
|
+
|
|
3
|
+
function required(name) {
|
|
4
|
+
const value = String(environment[name] ?? "").trim();
|
|
5
|
+
if (!value) {
|
|
6
|
+
throw new Error(`Missing ${name}`);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const username = required("MONGO_PROVIDER_USERNAME");
|
|
12
|
+
const password = required("MONGO_PROVIDER_PASSWORD");
|
|
13
|
+
const admin = db.getSiblingDB("admin");
|
|
14
|
+
const existing = admin.getUser(username);
|
|
15
|
+
const roles = [{ role: "readWriteAnyDatabase", db: "admin" }];
|
|
16
|
+
|
|
17
|
+
if (existing) {
|
|
18
|
+
admin.updateUser(username, { pwd: password, roles });
|
|
19
|
+
} else {
|
|
20
|
+
admin.createUser({ user: username, pwd: password, roles });
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Cache only generic capability routes. Provider Cache-Control headers remain
|
|
2
|
+
# authoritative, so private and no-store responses are never retained.
|
|
3
|
+
proxy_cache_path /var/cache/nginx/cms levels=1:2 keys_zone=cms_capabilities:16m max_size=1g inactive=1h use_temp_path=off;
|
|
4
|
+
|
|
5
|
+
map $uri $cms_skip_capability_cache {
|
|
6
|
+
default 1;
|
|
7
|
+
~^/(?:[^/]+/)*\.cms/call/ 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
map $request_method $cms_cache_method {
|
|
11
|
+
default $request_method;
|
|
12
|
+
HEAD GET;
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Included inside every generated proxy location by nginx-proxy.
|
|
2
|
+
proxy_cache cms_capabilities;
|
|
3
|
+
proxy_cache_methods GET HEAD;
|
|
4
|
+
proxy_cache_key "$scheme|$cms_cache_method|$host|$uri|$is_args$args";
|
|
5
|
+
proxy_cache_bypass $cms_skip_capability_cache $http_authorization;
|
|
6
|
+
proxy_no_cache $cms_skip_capability_cache $http_authorization;
|
|
7
|
+
proxy_cache_valid 200 206 1h;
|
|
8
|
+
add_header X-Cache-Status $upstream_cache_status always;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Never record query strings. Signed file access tokens are carried in queries.
|
|
2
|
+
log_format cms_query_safe '$remote_addr - $remote_user [$time_local] "$request_method $uri $server_protocol" '
|
|
3
|
+
'$status $body_bytes_sent "$http_referer" "$http_user_agent"';
|
|
4
|
+
access_log /var/log/nginx/access.log cms_query_safe;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
services:
|
|
2
|
+
proxy:
|
|
3
|
+
ports:
|
|
4
|
+
- "${ULVIA_BIND_ADDRESS}:${ULVIA_HTTPS_PORT}:443"
|
|
5
|
+
|
|
6
|
+
acme:
|
|
7
|
+
image: nginxproxy/acme-companion:2.8.1@sha256:ccfceb9798b9f6586d47eeeadbb8e185c8897b34e011a2739a9b82d5e92a8b6c
|
|
8
|
+
restart: unless-stopped
|
|
9
|
+
depends_on:
|
|
10
|
+
proxy:
|
|
11
|
+
condition: service_healthy
|
|
12
|
+
volumes_from:
|
|
13
|
+
- proxy
|
|
14
|
+
volumes:
|
|
15
|
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
16
|
+
- ${ULVIA_PROXY_ACME_DIR}:/etc/acme.sh
|
|
17
|
+
environment:
|
|
18
|
+
DEFAULT_EMAIL: ${ULVIA_ACME_EMAIL}
|
|
19
|
+
networks:
|
|
20
|
+
- instances
|