@cyaxis/ulvia 0.0.0-stage → 0.0.1-alpha.9
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/LICENSE +21 -0
- package/README.md +232 -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 +29600 -0
- package/package.json +24 -5
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,233 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Ulvia CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ulvia installation and release CLI.
|
|
4
|
+
|
|
5
|
+
The workspace package contains private source code. Official releases are
|
|
6
|
+
bundled without workspace dependencies and published publicly as
|
|
7
|
+
`@cyaxis/ulvia`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add --global @cyaxis/ulvia@next
|
|
11
|
+
ulvia --help
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
From the workspace, build and publish one exact release with:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun run cli:build --version 0.0.1-alpha.9
|
|
18
|
+
bun run cli:publish --version 0.0.1-alpha.9
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The build creates a checked npm archive and SHA-256 receipt below
|
|
22
|
+
`artifacts/cli`. Publishing accepts only that exact pair, targets the public npm
|
|
23
|
+
registry, and uses the `next` tag for prereleases or `latest` for stable versions.
|
|
24
|
+
It requires npm credentials configured outside the repository.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun run ulvia -- start
|
|
28
|
+
bun run ulvia -- configure network
|
|
29
|
+
bun run ulvia -- services status
|
|
30
|
+
bun run ulvia -- instance create preview --label "Preview site"
|
|
31
|
+
bun run ulvia -- instance list
|
|
32
|
+
bun run ulvia -- instance credentials preview
|
|
33
|
+
bun run ulvia -- instance stop preview
|
|
34
|
+
bun run ulvia -- instance delete preview --yes
|
|
35
|
+
bun run ulvia -- services stop
|
|
36
|
+
bun run ulvia -- upgrade kernel --version 0.0.1-alpha.9
|
|
37
|
+
bun run ulvia -- upgrade provider --version 0.0.1-alpha.9
|
|
38
|
+
bun run ulvia -- upgrade repository --version 0.0.1-alpha.9
|
|
39
|
+
bun run ulvia -- reset --yes
|
|
40
|
+
bun run ulvia -- release packages/official-repository/collections/ulvia-official
|
|
41
|
+
bun run ulvia -- release /path/to/contract-directory
|
|
42
|
+
bun run ulvia -- release /path/to/provider-directory
|
|
43
|
+
bun run ulvia -- push contract ulvia.official/ulvia.cms.installations@1.0.0 --repository https://repository.example
|
|
44
|
+
bun run ulvia -- pull collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example
|
|
45
|
+
bun run ulvia -- yank collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example --reason "Superseded"
|
|
46
|
+
bun run ulvia -- restore collection ulvia.official/ulvia-official@1.0.0 --repository https://repository.example
|
|
47
|
+
bun run ulvia -- prune
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The lifecycle commands manage the same Docker Compose projects for loopback and
|
|
51
|
+
public installations from any directory. Persistent
|
|
52
|
+
data lives below `$XDG_DATA_HOME/ulvia` or `~/.local/share/ulvia`;
|
|
53
|
+
`ULVIA_DATA_DIR` remains an explicit isolation override for tests and CI. The
|
|
54
|
+
CLI stores authored releases in `<data>/repository`. Shared Provider, Repository,
|
|
55
|
+
MongoDB, proxy, files, secrets and logs live below `<data>/services`; each
|
|
56
|
+
stateless Kernel has independent state and logs below `<data>/instances/<name>`.
|
|
57
|
+
The CLI never starts application servers, supervises their PIDs, or builds
|
|
58
|
+
software images. It pulls only the canonical public images under
|
|
59
|
+
`docker.io/cyaxis`, validates their Ulvia component and stack-API labels, and
|
|
60
|
+
stores the immutable repository digest selected for each component.
|
|
61
|
+
|
|
62
|
+
The first lifecycle command creates a safe installation configuration bound to
|
|
63
|
+
`127.0.0.1:5100`. Configure public exposure before creating an instance:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ulvia configure network \
|
|
67
|
+
--bind 0.0.0.0 \
|
|
68
|
+
--http-port 80 \
|
|
69
|
+
--tls acme \
|
|
70
|
+
--https-port 443 \
|
|
71
|
+
--acme-email ops@example.com
|
|
72
|
+
ulvia start
|
|
73
|
+
ulvia instance create website --domain example.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The configuration is persisted under `<data>/services/installation.json` and
|
|
77
|
+
is not a per-start mode. ACME requires `0.0.0.0` and every instance in an ACME
|
|
78
|
+
installation requires an explicit DNS domain. Network configuration is locked
|
|
79
|
+
after the first instance because its canonical Kernel URL is part of Provider
|
|
80
|
+
provisioning. Repository and Provider management ports remain loopback-only.
|
|
81
|
+
|
|
82
|
+
`services start` starts only the shared proxy, MongoDB, Repository and Provider.
|
|
83
|
+
`instance create` provisions one isolated Provider tenant and immediately starts
|
|
84
|
+
its dedicated Kernel. `instance start` restarts only an already-provisioned
|
|
85
|
+
instance's Kernel. All Kernels use the shared proxy port: an instance named
|
|
86
|
+
`default` is available at `http://localhost:5100`, while any other instance such
|
|
87
|
+
as `preview` uses `http://preview.localhost:5100`.
|
|
88
|
+
`ULVIA_HTTP_PORT`, `ULVIA_REPOSITORY_PORT`, and `ULVIA_PROVIDER_PORT` provide
|
|
89
|
+
initial isolation defaults for tests and CI. Interactive configuration belongs
|
|
90
|
+
to `ulvia configure network`. The Provider still listens on its canonical port
|
|
91
|
+
5103 inside the private Docker network; only its operator port on the host
|
|
92
|
+
changes.
|
|
93
|
+
`start` is the idempotent convenience command for shared services plus every
|
|
94
|
+
previously created local instance; it never creates an implicit instance.
|
|
95
|
+
|
|
96
|
+
The lifecycle boundary is deliberate:
|
|
97
|
+
|
|
98
|
+
| Command | Provider tenant | Kernel container |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `services start` | Does not create one | Does not start one |
|
|
101
|
+
| `instance create <name>` | Provisions one durable tenant | Creates and starts its Kernel |
|
|
102
|
+
| `instance start <name>` | Reuses the existing tenant | Starts only that Kernel |
|
|
103
|
+
| `start` | Never provisions | Starts every existing Kernel after shared services |
|
|
104
|
+
|
|
105
|
+
`instance credentials <name>` prints only that administrator's email and
|
|
106
|
+
password. Repository, Provider operator and instance bearer tokens remain in
|
|
107
|
+
private runtime files. Shared services and every instance have independent
|
|
108
|
+
`stop`, `status`, and `logs` boundaries. Software upgrades are explicit and
|
|
109
|
+
component-scoped through `upgrade <component> --version <exact-version>`.
|
|
110
|
+
Normal starts always reuse the installed digest and never follow a newer tag.
|
|
111
|
+
`reset --yes`
|
|
112
|
+
removes every Compose project, instance, shared state and repository
|
|
113
|
+
release. It does not inspect or modify the current working directory.
|
|
114
|
+
`release <resource-directory>` reads `definition.json` and routes by `kind`:
|
|
115
|
+
`collection`, `contract`, or `provider-manifest`. It validates and stores one
|
|
116
|
+
immutable release. A provider manifest must reference exact contract releases
|
|
117
|
+
already in the local repository. Release does not connect to or test a provider.
|
|
118
|
+
Contract fixture assets declared in `definition.json` are read from
|
|
119
|
+
`fixtures/<asset-id>` and verified by digest. Releasing the same coordinate with
|
|
120
|
+
different content fails. The local Repository serves stored resources
|
|
121
|
+
at `/v1/collections`, `/v1/contracts`, and `/v1/providers` on loopback port
|
|
122
|
+
5102, with declared contract fixtures at
|
|
123
|
+
`/v1/contracts/<publisher>/<contract>/<version>/fixtures/<asset-id>`, and sees
|
|
124
|
+
new releases without restarting. The CMS consumes the collection and provider
|
|
125
|
+
repository endpoints. The Provider administration Page imports an exact
|
|
126
|
+
manifest, approves an account connection and replaces the site's exact contract
|
|
127
|
+
selections for Gateway calls. `prune` empties all local repository content
|
|
128
|
+
without deleting service or instance data.
|
|
129
|
+
|
|
130
|
+
Contract authors may keep only release metadata in `definition.json` and split
|
|
131
|
+
capabilities recursively below `capabilities/`. Each capability is one JSON
|
|
132
|
+
object. Optional `mocks/**/*.json` fragments contain one mock plus its explicit
|
|
133
|
+
`capabilityId`; mocks embedded in capability fragments reject. The compiler
|
|
134
|
+
sorts capabilities and mocks by their declared IDs, so paths never affect the
|
|
135
|
+
immutable digest. `definition.json` cannot contain an inline `capabilities`
|
|
136
|
+
array: the recursive source tree is the only authored contract format.
|
|
137
|
+
|
|
138
|
+
An optional conformance companion uses `conformance/definition.json`, recursive
|
|
139
|
+
`conformance/scenarios/**/*.json` scenario objects and recursive
|
|
140
|
+
`conformance/exemptions/**/*.json` coverage-exemption objects. Optional suite
|
|
141
|
+
fixture bytes live below `conformance/fixtures/<asset-id>`. The release command
|
|
142
|
+
assembles and admits a present suite, including exact locally released
|
|
143
|
+
dependencies, before storing the contract. Conformance publication and live
|
|
144
|
+
execution are separate Gateway/repository APIs; the CLI does not yet orchestrate
|
|
145
|
+
a disposable provider environment.
|
|
146
|
+
|
|
147
|
+
`push` publishes one exact local coordinate, and `pull` downloads and re-admits
|
|
148
|
+
one exact remote coordinate before storing it locally. Both support
|
|
149
|
+
`collection`, `contract`, and `provider`; dependencies must be transferred
|
|
150
|
+
first. `ULVIA_REPOSITORY_URL` can replace `--repository`.
|
|
151
|
+
Repository writes require `ULVIA_REPOSITORY_TOKEN`; the token is never accepted
|
|
152
|
+
as a command-line argument. Every mutation carries a timestamp, one-time nonce,
|
|
153
|
+
body digest, and HMAC-SHA-256 signature. The server rejects stale or replayed
|
|
154
|
+
requests, serializes writes across processes, re-runs admission and evolution
|
|
155
|
+
rules, and stages every declared asset as a separately signed raw stream. A final
|
|
156
|
+
idempotent commit makes the immutable artifact visible only after all byte lengths
|
|
157
|
+
and SHA-256 digests pass. Interrupted and rejected sessions never expose a partial
|
|
158
|
+
release. An exact republish is idempotent; different bytes at an existing coordinate
|
|
159
|
+
reject.
|
|
160
|
+
|
|
161
|
+
The protocol, exact-coordinate client and mutation endpoint live in
|
|
162
|
+
`@bernouy/cms-repository/repository/publication`; the CLI only composes them with
|
|
163
|
+
the reference filesystem adapter and its loopback listener. The filesystem
|
|
164
|
+
composition persists upload sessions and replay claims below the repository root.
|
|
165
|
+
A production repository can replace those ports without depending on this
|
|
166
|
+
executable package.
|
|
167
|
+
|
|
168
|
+
`yank` is reversible catalogue metadata: it hides a release from new catalogue
|
|
169
|
+
resolution without deleting its exact historical bytes. `restore` exposes it
|
|
170
|
+
again. Contract yanks block new dependent publications while previously
|
|
171
|
+
published provider manifests remain readable. The loopback Repository uses the
|
|
172
|
+
same write protocol and keeps its generated repository token in the private
|
|
173
|
+
runtime directory. The removed integration repository/Supabase bridge is not
|
|
174
|
+
run.
|
|
175
|
+
|
|
176
|
+
A collection keeps immutable administration copy in recursive locale
|
|
177
|
+
directories such as `translations/en/collection.json` and
|
|
178
|
+
`translations/en/theme/colors.json`. Every JSON object below
|
|
179
|
+
`translations/<locale>/` contributes to that locale's flat catalogue.
|
|
180
|
+
Collection, Bloc, setting, text, theme and Page metadata stores
|
|
181
|
+
reusable keys from those catalogues rather than inline labels. A key may occur
|
|
182
|
+
in only one fragment per locale. The collection locale must define every
|
|
183
|
+
referenced key; additional locale directories may be partial and fall back to
|
|
184
|
+
it.
|
|
185
|
+
|
|
186
|
+
A collection theme is authored as an ordered set of files. Its
|
|
187
|
+
`theme/definition.json` contains the theme label key and the ordered list of
|
|
188
|
+
category IDs. Category files may be organized recursively below `theme/`; the
|
|
189
|
+
scanner finds them by their internal `id`, while the manifest remains the only
|
|
190
|
+
source of display order. A category filename must match its ID, duplicate IDs
|
|
191
|
+
and orphan files reject, and category/token metadata uses the shared translation
|
|
192
|
+
keys. Categories use an `entries` array containing ordinary tokens and optional
|
|
193
|
+
typed `font-face` entries backed by local WOFF2 assets.
|
|
194
|
+
|
|
195
|
+
Collection assets are discovered recursively below `assets/`; their lowercase
|
|
196
|
+
relative paths are their stable IDs. They are not declared in the collection
|
|
197
|
+
`definition.json`. An optional root `assets/definition.json` accepts only a
|
|
198
|
+
`generations` object keyed by those paths. The compiler derives media type,
|
|
199
|
+
size and digest. Safe presentation-only SVG files receive the `inline-svg`
|
|
200
|
+
profile automatically; other SVGs remain downloadable assets but cannot be
|
|
201
|
+
expanded into authored Page DOM.
|
|
202
|
+
|
|
203
|
+
Collection texts use recursive `texts/definitions/**/*.json` arrays for stable
|
|
204
|
+
IDs and administration metadata, plus recursive
|
|
205
|
+
`texts/locales/<locale>/**/*.json` objects for content values. The release
|
|
206
|
+
command joins them into ordinary collection text records. Paths never prefix a
|
|
207
|
+
text ID; duplicate definitions, duplicate per-locale values and unknown value
|
|
208
|
+
IDs reject.
|
|
209
|
+
|
|
210
|
+
Bloc roots are discovered recursively below `blocs/`. A folder becomes a Bloc
|
|
211
|
+
when it contains `definition.json`, its basename must match the declared Bloc
|
|
212
|
+
ID, and discovery stops there so `settings/definition.json` is never mistaken
|
|
213
|
+
for a nested Bloc. Intermediate grouping directories contain only directories
|
|
214
|
+
and do not affect release identity.
|
|
215
|
+
|
|
216
|
+
The Official Repository image contains the admitted CMS contracts, provider
|
|
217
|
+
manifest, and administration collection. It installs that snapshot only into
|
|
218
|
+
empty persistent storage. To publish the same resources manually, release the
|
|
219
|
+
CMS contracts before the manifest:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.access
|
|
223
|
+
bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.installations
|
|
224
|
+
bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.content
|
|
225
|
+
bun run ulvia -- release packages/official-repository/contracts/ulvia.cms.files
|
|
226
|
+
bun run ulvia -- release packages/official-repository/contracts/ulvia.provider.cms-instances
|
|
227
|
+
bun run ulvia -- release packages/official-repository/providers/ulvia.official
|
|
228
|
+
bun run ulvia -- start
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`instance create` provisions the named instance through the Provider operator
|
|
232
|
+
API, then starts its Kernel. Connection review validates the runtime report and
|
|
233
|
+
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
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
services:
|
|
2
|
+
proxy:
|
|
3
|
+
image: nginxproxy/nginx-proxy:1.11.5@sha256:e06db5fb9586234e9c2bdef707c8b8d784caa4fb49fbe7285eb3730a501c3eaa
|
|
4
|
+
restart: unless-stopped
|
|
5
|
+
ports:
|
|
6
|
+
- "${ULVIA_BIND_ADDRESS}:${ULVIA_HTTP_PORT}:80"
|
|
7
|
+
volumes:
|
|
8
|
+
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
9
|
+
- ./nginx/client_max_body.conf:/etc/nginx/conf.d/zzz_client_max_body.conf:ro
|
|
10
|
+
- ./nginx/capability_cache.conf:/etc/nginx/conf.d/zzz_capability_cache.conf:ro
|
|
11
|
+
- ./nginx/query_safe_access_log.conf:/etc/nginx/conf.d/zzz_query_safe_access_log.conf:ro
|
|
12
|
+
- ./nginx/default_location:/etc/nginx/vhost.d/default_location:ro
|
|
13
|
+
- ${ULVIA_PROXY_CERTIFICATES_DIR}:/etc/nginx/certs
|
|
14
|
+
- ${ULVIA_PROXY_VHOST_DIR}:/etc/nginx/vhost.d
|
|
15
|
+
- ${ULVIA_PROXY_CHALLENGE_DIR}:/usr/share/nginx/html
|
|
16
|
+
- ${ULVIA_PROXY_ACME_DIR}:/etc/acme.sh
|
|
17
|
+
- proxy-cache:/var/cache/nginx
|
|
18
|
+
networks:
|
|
19
|
+
- instances
|
|
20
|
+
healthcheck:
|
|
21
|
+
test: ["CMD", "nginx", "-t"]
|
|
22
|
+
interval: 30s
|
|
23
|
+
timeout: 5s
|
|
24
|
+
retries: 3
|
|
25
|
+
start_period: 5s
|
|
26
|
+
|
|
27
|
+
mongo:
|
|
28
|
+
image: mongo:7.0.37@sha256:d5b3ca8c3f3cdce78d44870dc0871b76d5235e9b2ad4ea6bea5d1fbff8027703
|
|
29
|
+
restart: unless-stopped
|
|
30
|
+
user: "${ULVIA_UID}:${ULVIA_GID}"
|
|
31
|
+
environment:
|
|
32
|
+
MONGO_INITDB_ROOT_USERNAME: ${ULVIA_MONGO_USERNAME}
|
|
33
|
+
MONGO_INITDB_ROOT_PASSWORD: ${ULVIA_MONGO_PASSWORD}
|
|
34
|
+
volumes:
|
|
35
|
+
- type: bind
|
|
36
|
+
source: ${ULVIA_MONGO_DIR}
|
|
37
|
+
target: /data/db
|
|
38
|
+
networks:
|
|
39
|
+
- private
|
|
40
|
+
healthcheck:
|
|
41
|
+
test:
|
|
42
|
+
[
|
|
43
|
+
"CMD-SHELL",
|
|
44
|
+
"mongosh --quiet --username \"$$MONGO_INITDB_ROOT_USERNAME\" --password \"$$MONGO_INITDB_ROOT_PASSWORD\" --authenticationDatabase admin --eval 'quit(db.adminCommand({ ping: 1 }).ok ? 0 : 1)'",
|
|
45
|
+
]
|
|
46
|
+
interval: 30s
|
|
47
|
+
timeout: 5s
|
|
48
|
+
retries: 3
|
|
49
|
+
start_period: 10s
|
|
50
|
+
stop_grace_period: 30s
|
|
51
|
+
|
|
52
|
+
repository:
|
|
53
|
+
image: ${OFFICIAL_REPOSITORY_IMAGE}
|
|
54
|
+
restart: unless-stopped
|
|
55
|
+
init: true
|
|
56
|
+
user: "${ULVIA_UID}:${ULVIA_GID}"
|
|
57
|
+
environment:
|
|
58
|
+
MODE: PROD
|
|
59
|
+
NODE_ENV: production
|
|
60
|
+
REPOSITORY_HOST: 0.0.0.0
|
|
61
|
+
REPOSITORY_PORT: "3000"
|
|
62
|
+
ULVIA_LOG_FILE: /var/log/ulvia/repository.ndjson
|
|
63
|
+
ULVIA_REPOSITORY_DIR: /var/lib/ulvia-repository
|
|
64
|
+
ULVIA_REPOSITORY_SEED_DIR: /opt/ulvia-repository-seed
|
|
65
|
+
ULVIA_REPOSITORY_TOKEN: ${ULVIA_REPOSITORY_TOKEN}
|
|
66
|
+
volumes:
|
|
67
|
+
- type: bind
|
|
68
|
+
source: ${ULVIA_REPOSITORY_DIR}
|
|
69
|
+
target: /var/lib/ulvia-repository
|
|
70
|
+
- type: bind
|
|
71
|
+
source: ${ULVIA_LOGS_DIR}
|
|
72
|
+
target: /var/log/ulvia
|
|
73
|
+
ports:
|
|
74
|
+
- "127.0.0.1:${ULVIA_REPOSITORY_PORT}:3000"
|
|
75
|
+
networks:
|
|
76
|
+
- private
|
|
77
|
+
read_only: true
|
|
78
|
+
tmpfs:
|
|
79
|
+
- /tmp:rw,nosuid,nodev,noexec,size=64m
|
|
80
|
+
cap_drop: [ALL]
|
|
81
|
+
security_opt: [no-new-privileges:true]
|
|
82
|
+
healthcheck:
|
|
83
|
+
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()"]
|
|
84
|
+
interval: 30s
|
|
85
|
+
timeout: 5s
|
|
86
|
+
retries: 3
|
|
87
|
+
start_period: 5s
|
|
88
|
+
|
|
89
|
+
mongo-provision:
|
|
90
|
+
image: mongo:7.0.37@sha256:d5b3ca8c3f3cdce78d44870dc0871b76d5235e9b2ad4ea6bea5d1fbff8027703
|
|
91
|
+
restart: "no"
|
|
92
|
+
environment:
|
|
93
|
+
MONGO_INITDB_ROOT_USERNAME: ${ULVIA_MONGO_USERNAME}
|
|
94
|
+
MONGO_INITDB_ROOT_PASSWORD: ${ULVIA_MONGO_PASSWORD}
|
|
95
|
+
MONGO_PROVIDER_USERNAME: ${ULVIA_MONGO_PROVIDER_USERNAME}
|
|
96
|
+
MONGO_PROVIDER_PASSWORD: ${ULVIA_MONGO_PROVIDER_PASSWORD}
|
|
97
|
+
entrypoint: ["/bin/sh", "-c"]
|
|
98
|
+
command:
|
|
99
|
+
- >-
|
|
100
|
+
exec mongosh --quiet --host mongo
|
|
101
|
+
--username "$$MONGO_INITDB_ROOT_USERNAME"
|
|
102
|
+
--password "$$MONGO_INITDB_ROOT_PASSWORD"
|
|
103
|
+
--authenticationDatabase admin
|
|
104
|
+
/opt/ulvia-mongo/provision-provider.js
|
|
105
|
+
volumes:
|
|
106
|
+
- ./mongo/provision-provider.js:/opt/ulvia-mongo/provision-provider.js:ro
|
|
107
|
+
networks:
|
|
108
|
+
- private
|
|
109
|
+
depends_on:
|
|
110
|
+
mongo:
|
|
111
|
+
condition: service_healthy
|
|
112
|
+
read_only: true
|
|
113
|
+
tmpfs:
|
|
114
|
+
- /tmp:rw,nosuid,nodev,noexec,size=32m
|
|
115
|
+
cap_drop: [ALL]
|
|
116
|
+
security_opt: [no-new-privileges:true]
|
|
117
|
+
|
|
118
|
+
provider:
|
|
119
|
+
image: ${PROVIDER_IMAGE}
|
|
120
|
+
restart: unless-stopped
|
|
121
|
+
init: true
|
|
122
|
+
user: "${ULVIA_UID}:${ULVIA_GID}"
|
|
123
|
+
environment:
|
|
124
|
+
MODE: PROD
|
|
125
|
+
NODE_ENV: production
|
|
126
|
+
CMS_FILES_DIR: /var/lib/cms/files
|
|
127
|
+
CMS_REPOSITORY_URL: http://repository:3000
|
|
128
|
+
CMS_REPOSITORY_ALLOW_INSECURE_HTTP: "true"
|
|
129
|
+
CMS_SESSION_SECRET: ${ULVIA_PROVIDER_SESSION_SECRET}
|
|
130
|
+
CMS_KEK_HEX: ${ULVIA_PROVIDER_KEK_HEX}
|
|
131
|
+
MONGO_URL: mongodb://${ULVIA_MONGO_PROVIDER_USERNAME}:${ULVIA_MONGO_PROVIDER_PASSWORD}@mongo:27017/ulvia_provider_control?authSource=admin&retryWrites=false
|
|
132
|
+
ULVIA_LOG_FILE: /var/log/ulvia/provider.ndjson
|
|
133
|
+
ULVIA_PROVIDER_HOST: 0.0.0.0
|
|
134
|
+
ULVIA_PROVIDER_PORT: "5103"
|
|
135
|
+
ULVIA_PROVIDER_PUBLIC_URL: http://ulvia-provider:5103
|
|
136
|
+
ULVIA_PROVIDER_OPERATOR_TOKEN: ${ULVIA_PROVIDER_OPERATOR_TOKEN}
|
|
137
|
+
volumes:
|
|
138
|
+
- type: bind
|
|
139
|
+
source: ${ULVIA_FILES_DIR}
|
|
140
|
+
target: /var/lib/cms/files
|
|
141
|
+
- type: bind
|
|
142
|
+
source: ${ULVIA_LOGS_DIR}
|
|
143
|
+
target: /var/log/ulvia
|
|
144
|
+
ports:
|
|
145
|
+
- "127.0.0.1:${ULVIA_PROVIDER_PORT}:5103"
|
|
146
|
+
networks:
|
|
147
|
+
private: {}
|
|
148
|
+
instances:
|
|
149
|
+
aliases:
|
|
150
|
+
- ulvia-provider
|
|
151
|
+
depends_on:
|
|
152
|
+
mongo-provision:
|
|
153
|
+
condition: service_completed_successfully
|
|
154
|
+
repository:
|
|
155
|
+
condition: service_healthy
|
|
156
|
+
read_only: true
|
|
157
|
+
tmpfs:
|
|
158
|
+
- /tmp:rw,nosuid,nodev,noexec,size=256m
|
|
159
|
+
cap_drop: [ALL]
|
|
160
|
+
security_opt: [no-new-privileges:true]
|
|
161
|
+
healthcheck:
|
|
162
|
+
test: ["CMD", "bun", "-e", "const r=await fetch('http://127.0.0.1:5103/ulvia/operator/instances',{headers:{authorization:'Bearer '+process.env.ULVIA_PROVIDER_OPERATOR_TOKEN}}); if(!r.ok) process.exit(1); await r.body?.cancel()"]
|
|
163
|
+
interval: 30s
|
|
164
|
+
timeout: 5s
|
|
165
|
+
retries: 3
|
|
166
|
+
start_period: 10s
|
|
167
|
+
stop_grace_period: 30s
|
|
168
|
+
|
|
169
|
+
networks:
|
|
170
|
+
private:
|
|
171
|
+
name: ${ULVIA_PRIVATE_NETWORK}
|
|
172
|
+
internal: true
|
|
173
|
+
instances:
|
|
174
|
+
name: ${ULVIA_INSTANCE_NETWORK}
|
|
175
|
+
|
|
176
|
+
volumes:
|
|
177
|
+
proxy-cache:
|