@or3/cloud 0.1.15 → 0.1.32

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/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # OR3 Cloud
2
+
3
+ Install and operate a supported OR3 container deployment without cloning the
4
+ application repository or installing its dependencies on the host.
5
+
6
+ ## Quick start: local machine
7
+
8
+ You need Docker Engine with Docker Compose v2 and Node.js 20 or later.
9
+
10
+ ```sh
11
+ npx @or3/cloud init --local
12
+ ```
13
+
14
+ The installer asks for the owner email, generates a bootstrap password, and
15
+ saves both values in the mode-`0600`
16
+ `or3-cloud/.or3-initial-credentials` file. It does not print the password.
17
+ Save the file in a password manager, then delete it. Open
18
+ [http://127.0.0.1:3000](http://127.0.0.1:3000) and sign in.
19
+
20
+ The published Cloud image supports `linux/amd64` and `linux/arm64` and uses
21
+ Basic Auth + SQLite + filesystem storage. The CLI checks the image manifest
22
+ before it writes deployment state.
23
+
24
+ ## Run it publicly on a VPS
25
+
26
+ Before starting, point a domain at the VPS and allow inbound TCP ports `80`
27
+ and `443` in its firewall. Then run:
28
+
29
+ ```sh
30
+ npx @or3/cloud init --public --domain cloud.example.com
31
+ ```
32
+
33
+ OR3 Cloud uses Caddy to obtain and renew HTTPS certificates. It does not
34
+ change your DNS, firewall, Cloudflare, or Tailscale settings.
35
+
36
+ ## Day-to-day commands
37
+
38
+ Run these from the deployment directory created by `init`:
39
+
40
+ ```sh
41
+ npx @or3/cloud doctor
42
+ npx @or3/cloud backup
43
+ npx @or3/cloud update
44
+ npx @or3/cloud rollback --yes
45
+ npx @or3/cloud recover
46
+ npx @or3/cloud status
47
+ npx @or3/cloud logs --tail 200
48
+ npx @or3/cloud start
49
+ npx @or3/cloud stop
50
+ npx @or3/cloud restart
51
+ ```
52
+
53
+ Do not run `docker compose down --volumes` on a normal deployment: it deletes
54
+ the application data. Use `backup` before an update or any destructive action.
55
+ For a destructive purge, export a fresh backup to a new directory on another
56
+ filesystem first; `remove --purge-data --yes` verifies that export before it
57
+ will delete anything.
58
+
59
+ Managed registration is invite-only: the bootstrap owner signs in first and
60
+ invites additional users from the in-product admin flow. Guest access and
61
+ anonymous registration stay disabled. Uploading/installing custom plugins or
62
+ themes is disabled in the immutable image because it cannot rebuild trusted
63
+ source; bundled extensions remain available.
64
+
65
+ Remote OR3 Connect is withheld from the managed Cloud release until its
66
+ Cloudflare/domain staging flow is proved. The intended local Intern command is
67
+ `npx @or3/connect intern`, but it is usable only after the matching package and
68
+ verified release assets are published; see the [Connect release status](https://github.com/Saluana/or3-intern/blob/main/docs/connect-release-status.md).
69
+ Do not substitute the older package or paste a generic remote URL/token as a
70
+ workaround. The [Start Here guide](https://github.com/Saluana/or3-chat/blob/main/docs/start-here.md)
71
+ routes each supported setup path.
72
+
73
+ ## More help
74
+
75
+ ```sh
76
+ npx @or3/cloud --help
77
+ ```
78
+
79
+ For VPS requirements, firewall guidance, updates, restores, and migration,
80
+ read the [installation guide](https://github.com/Saluana/or3-chat/blob/main/docs/installation.md).
package/assets/Caddyfile CHANGED
@@ -1,4 +1,15 @@
1
1
  {$OR3_PUBLIC_DOMAIN} {
2
+ # Security headers. Caddy 308-redirects plain HTTP to HTTPS before this
3
+ # site handler runs, so Strict-Transport-Security is only ever emitted on
4
+ # HTTPS responses (it is meaningless on plain HTTP).
5
+ header {
6
+ Strict-Transport-Security "max-age=31536000; includeSubDomains"
7
+ X-Content-Type-Options "nosniff"
8
+ X-Frame-Options "DENY"
9
+ Referrer-Policy "strict-origin-when-cross-origin"
10
+ Permissions-Policy "camera=(), microphone=(), geolocation=()"
11
+ }
12
+
2
13
  encode zstd gzip
3
14
  reverse_proxy or3:3000 {
4
15
  # Preserve incremental SSE/chat output instead of buffering it at the proxy.
@@ -1,8 +1,13 @@
1
1
  services:
2
2
  caddy:
3
- image: caddy:2.10-alpine
3
+ # Pinned to the exact patch release and image digest so a floating tag
4
+ # cannot silently change proxy behavior or the base image.
5
+ image: caddy:2.10.2-alpine@sha256:4c6e91c6ed0e2fa03efd5b44747b625fec79bc9cd06ac5235a779726618e530d
4
6
  depends_on:
5
7
  or3:
8
+ # This gates Caddy's initial startup only. A later unhealthy OR3
9
+ # healthcheck does not trigger automatic container restarts; use the
10
+ # Cloud CLI's doctor/status/restart flow for persistent degradation.
6
11
  condition: service_healthy
7
12
  environment:
8
13
  OR3_PUBLIC_DOMAIN: ${OR3_PUBLIC_DOMAIN}
@@ -15,6 +20,26 @@ services:
15
20
  - caddy-data:/data
16
21
  - caddy-config:/config
17
22
  restart: unless-stopped
23
+ # Container hardening. The official image runs as root (no caddy user) and
24
+ # /data + /config are root-owned, so the process stays root but all
25
+ # capabilities are dropped except NET_BIND_SERVICE, which is required to
26
+ # bind the privileged ports 80/443. The root filesystem is read-only;
27
+ # certificates/state live on the caddy-data/caddy-config volumes and
28
+ # scratch space is a small noexec tmpfs at /tmp.
29
+ security_opt:
30
+ - no-new-privileges:true
31
+ cap_drop:
32
+ - ALL
33
+ cap_add:
34
+ - NET_BIND_SERVICE
35
+ read_only: true
36
+ tmpfs:
37
+ - /tmp:size=64m,noexec,nosuid
38
+ logging:
39
+ driver: json-file
40
+ options:
41
+ max-size: "10m"
42
+ max-file: "3"
18
43
 
19
44
  volumes:
20
45
  caddy-data:
@@ -18,12 +18,40 @@ services:
18
18
  volumes:
19
19
  - or3-data:/data
20
20
  restart: unless-stopped
21
+ # Container hardening: the runtime image already runs as a non-root user and
22
+ # listens on port 3000 (no privileged ports), so all capabilities are
23
+ # dropped and the root filesystem is read-only. Persistent state lives on
24
+ # the or3-data volume (/data: SQLite, storage, admin data, extensions);
25
+ # scratch space (SQLite WAL, H3 multipart uploads, Nuxt server caches)
26
+ # is a 256m tmpfs at /tmp. Phase-5 smokes must re-verify uploads and
27
+ # plugin installs against this read-only profile.
28
+ security_opt:
29
+ - no-new-privileges:true
30
+ cap_drop:
31
+ - ALL
32
+ read_only: true
33
+ tmpfs:
34
+ - /tmp:size=256m
35
+ logging:
36
+ driver: json-file
37
+ options:
38
+ max-size: "10m"
39
+ max-file: "3"
21
40
  healthcheck:
41
+ # Deep health, mirroring the CLI's HEALTH_SCRIPT: exit non-zero unless
42
+ # the response is HTTP ok AND the parsed body status is exactly 'ok'.
43
+ # A degraded provider (e.g. sync/storage/auth down) reports HTTP 200
44
+ # with status 'degraded' and is therefore marked unhealthy. Docker
45
+ # Compose uses that state for startup dependencies and `or3 status`
46
+ # reports it, but Docker does not restart a running container solely
47
+ # because a healthcheck fails; `restart: unless-stopped` covers process
48
+ # exits only. Operators should run `or3 doctor`/`or3 restart` for a
49
+ # persistent degraded state.
22
50
  test:
23
51
  - CMD
24
- - node
52
+ - /nodejs/bin/node
25
53
  - -e
26
- - "fetch('http://127.0.0.1:3000/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"
54
+ - "fetch('http://127.0.0.1:3000/api/health?deep=true').then(async r=>{const b=await r.json().catch(()=>({}));if(!r.ok||b.status!=='ok')process.exit(1)}).catch(()=>process.exit(1))"
27
55
  interval: 10s
28
56
  timeout: 5s
29
57
  retries: 12