summaryrefslogtreecommitdiff
path: root/docs/OPERATIONS.txt
blob: 55dc81ca08431996e6a2ff9097dfbee1159eb396 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

STACK OPERATIONS

    Root: ~/stack. Develop on local Linux; production: ops@23.155.44.67.
    Public source: https://git.doan.luuduct.ai/stack.
    GNU Affero General Public License, version 3 or later.

KEEP

    Full --include-images encrypted export: public source, compiled CLI, .env,
    persistent data, certificates, bare repos, working Git history if present,
    exact service images, checksum manifest. No old server filesystem needed.
    Excludes engine storage, caches, old exports, host packages/SSH keys/firewall,
    disks, OS configuration. Recreate host setup separately.
    Public Git alone cannot recover .env, live data, TLS keys, or var/ repos.

    Decryption needs GPG private key + passphrase. Keep key export/revocation
    certificate in KeePassXC; keep independent database copy. Server holds public
    key only. Never send private key/password through chat.
    Default recipient: 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6.

DEVELOP LOCAL

    From ~/stack on workstation:

        cargo stack dev
        cargo stack status
        cargo stack down

    Open http://localhost:8080, http://localhost:8080/git/,
    http://localhost:8080/search/. Alternate port:

        cargo stack dev --port 9080

    Local mode: loopback only, local links, unchanged public DNS, no public TLS.
    Missing .env: create with random SearXNG secret. Edit secrets there; never commit.
    infra/: service settings. web/: content. crates/: new backends; add workspace members.
    infra/services.toml declares services. Each gets only listed environment keys.

    Project engine storage: var/podman/. Bare 'podman ps' uses another store.
    Inspect project containers from ~/stack:

        podman --root "$PWD/var/podman/storage" --runroot "$PWD/var/podman/run" ps -a

    Keep both global options for 'logs CONTAINER', 'exec CONTAINER', 'images'.
    cargo stack status gives names. Never migrate var/podman/; export images.

CHECK LOCAL

    Install development audit tools once:

        cargo install --locked --version 0.20.2 cargo-deny --root .tools
        cargo install --locked --version 0.9.3 --features cli cargo-about --root .tools

        cargo stack check

    Checks formatting, all workspace Clippy targets/features, tests, private
    rustdoc, advisories, duplicate versions, sources, licenses. cargo-about
    regenerates THIRD-PARTY-LICENSES.html. Format with cargo fmt --all.
    Dependency changes: update Cargo.lock deliberately; run full check; commit
    lockfile and license page. Never disable audit failures to ship.

BACK UP PRODUCTION

    On VPS:

        cd ~/stack
        ./bin/stack backup --include-images

    Briefly pauses Caddy/cgit/SearXNG during file copy; resumes before compression
    and encryption. Chat stays live for SQLite online backup. Snapshots include
    chat and chat-local; exclude raw journals and server locks.
    Verifies public recipient fingerprint, checks bare repos, saves exact images:

        var/exports/stack-TIMESTAMP.tar.gz.gpg
        var/exports/stack-TIMESTAMP.tar.gz.gpg.sha256

    Restrictive permissions. Removes temporary plaintext on success/handled errors.
    No scheduled backup. Choose retention and off-host copies yourself.
    On workstation, copy actual filenames printed above:

        umask 077
        mkdir -p ~/stack/var/backups/production
        scp ops@23.155.44.67:stack/var/exports/stack-TIMESTAMP.tar.gz.gpg ~/stack/var/backups/production/
        scp ops@23.155.44.67:stack/var/exports/stack-TIMESTAMP.tar.gz.gpg.sha256 ~/stack/var/backups/production/
        cd ~/stack/var/backups/production
        sha256sum -c stack-TIMESTAMP.tar.gz.gpg.sha256

    Replace TIMESTAMP. Checksum detects corruption; keep trusted export + checksum.
    Keep another copy on separate device/location. After verification, optionally
    delete matching VPS export files only. Never delete var/data or .env.
    ./bin/stack backup without images needs registry/build access for local startup;
    not standalone production recovery. Prefer --include-images.

RESTORE ON LINUX

    Host tools: Podman, Git, GnuPG, rootless networking prerequisites.
    Target user needs >=65536 subordinate UIDs/GIDs. Use distro-supported Podman.
    Prebuilt binary/images: x86_64 Linux (linux/amd64).
    Copy export + checksum; verify checksum first. If needed, import key:

        gpg --import private-key-backup.asc

    GnuPG prompts locally. Never pass passphrase through arguments or .env.
    Existing compatible CLI:

        ./bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack-restored

    Destination must not exist. Restore decrypts privately; rejects unsafe paths
    and links; checks every manifest entry; assigns unique project identity;
    defaults local; starts nothing; never overwrites existing installation.

    No CLI/toolchain yet: bootstrap trusted export with GnuPG/tar. Bash/fish:

        umask 077
        mkdir -p ~/stack-bootstrap
        gpg --output ~/stack-bootstrap/export.tar.gz --decrypt /path/to/stack-TIMESTAMP.tar.gz.gpg
        tar -xzf ~/stack-bootstrap/export.tar.gz -C ~/stack-bootstrap stack/bin/stack
        ~/stack-bootstrap/stack/bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack

    Bootstrap executes extracted code: use only your trusted export.
    Rust restore validates full archive. After success, delete ~/stack-bootstrap:
    it holds decrypted archive. Retain encrypted backup.
    Test restored stack locally first:

        cd ~/stack
        ./bin/stack up --local --port 8080
        ./bin/stack status

    Saved images load automatically; image lock verifies identities. Matching
    architecture needs no registry pull/build. Check homepage, Git, search, WKD.
    ./bin/stack down before production setup.
    Same restore accepts release archives: source/binary/images, no .env/live data.
    Starting restored release creates fresh state; cannot recover lost data.

NEW PRODUCTION HOST

    Restore into target user's ~/stack. Debian: review infra/host-setup.sh;
    replace 'ops' if needed; run:

        sudo sh ~/stack/infra/host-setup.sh

    Installs Podman/network tools, checks existing subordinate ranges, enables
    user linger, sets:

        net.ipv4.ip_unprivileged_port_start=80

    System-wide rootless ports >=80; ports <80 stay privileged. SSH/firewall unchanged.
    Missing ranges: allocate unused ranges through distro account tools; rerun.
    Other distros: equivalent packages, sysctl, linger. Supplied boot unit needs
    systemd user manager.

    Host/provider firewall: allow 22/tcp, 80/tcp, 443/tcp, 443/udp for applicable
    IPv4/IPv6. Never publish backend container ports. Preserve SSH; verify address
    before changing DNS. A: new IPv4. AAAA: working new IPv6 only; remove stale AAAA.
    Hosts live in root .env; edit and reapply rendered settings. infra/Caddyfile
    is public template. Keys/render-only commands: CONFIGURATION.txt.
    Preserve www aliases and openpgpkey names pointing to canonical hosts.

        cd ~/stack
        ./bin/stack up --production

    Production binds 80/tcp, 443/tcp, 443/udp. Caddy issues/renews certificates,
    redirects HTTP to HTTPS and www to canonical URLs. State: var/data/caddy/.
    Public issuance needs working public DNS/connectivity.
    Enable boot startup:

        systemctl --user link ~/stack/infra/stack.service
        systemctl --user daemon-reload
        systemctl --user enable --now stack.service
        systemctl --user status stack.service

    Unit assumes ~/stack; edit paths for other location. Uses bin/stack.
    Linger keeps user service alive
    without login. Containers use unless-stopped restart policy.

    Optional former VPS SSH Git path; administrator only:

        sudo install -d -m 755 /srv/git
        sudo ln -s /home/ops/stack/var/data/repos/stack /srv/git/stack

    Never overwrite existing repo. Current VPS already has this layout link.
    Public HTTPS Git needs no host symlink.

BUILD AND DEPLOY

    Develop/check on workstation:

        cd ~/stack
        cargo stack release

    Release reruns full check; builds static Rust CLI/chat + browser WASM;
    packages matching source; builds cgit/chat containers; saves configured images.
    Output: dist/stack-release-TIMESTAMP.tar.gz + .sha256.
    Excludes .env, live data, .git history, previous backups.
    Restore into fresh local test directory; up --local on free port; inspect
    four services; stop test. Commit/push reviewed public changes:

        git add -A
        git diff --cached --stat
        git commit -S3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 -m 'Describe the change'
        git push origin main

    Review staged files. .gitignore excludes secrets/runtime/artifacts.
    Use RSA-2048 signed commits; author/key: POLICY.txt.
    Deploy archive, never git pull. Copy actual filenames:

        ssh ops@23.155.44.67 'mkdir -p ~/stack/var/incoming'
        scp dist/stack-release-TIMESTAMP.tar.gz ops@23.155.44.67:stack/var/incoming/
        scp dist/stack-release-TIMESTAMP.tar.gz.sha256 ops@23.155.44.67:stack/var/incoming/

    On VPS:

        cd ~/stack/var/incoming
        sha256sum -c stack-release-TIMESTAMP.tar.gz.sha256
        cd ~/stack
        mkdir -p var/incoming/bootstrap
        tar -xzf var/incoming/stack-release-TIMESTAMP.tar.gz -C var/incoming/bootstrap stack/bin/stack
        var/incoming/bootstrap/stack/bin/stack --root "$PWD" activate var/incoming/stack-release-TIMESTAMP.tar.gz
        ./bin/stack status
        rm -rf -- var/incoming/bootstrap

    Use verified NEW release binary: old binary may reject new service schema.
    Activation verifies archive/platform, replaces public source/binary, loads
    exact images, preserves .env/var/data, replaces running containers. No compiler,
    Rust build, image build, or private-config overwrite. Expect brief interruption.
    Take verified export before changes needing recovery. Verify production;
    optionally delete corresponding incoming archive/checksum.

RECOVERY LIMITS; ROTATE KEYS

    Application export is not bootable VPS image. Accounts, SSH, firewall,
    packages remain host duties. Other CPU architecture needs native release.
    Registry needed only without saved images or when rebuilding another target.
    State: files, Git repos, Caddy, encrypted SQLite chat. SQLite uses online backup.
    Recovery proof: reopen BOTH participants' unchanged identities; decrypt history.
    See CHAT-PRODUCTION.txt. Future databases need supported snapshot/recovery.

    New backup recipient: publish public key, update default fingerprint, test
    encryption/restore. Single export: --recipient FULL_FINGERPRINT --key /path/public-key.asc.
    Retain old secret keys while any retained export needs them.
    Restore accepts verified format-2 application archives.

LICENSE PAGE; REDIRECTS

    cargo-about generates web/THIRD-PARTY-LICENSES.html. Caddy serves canonical
    https://doan.luuduct.ai/THIRD-PARTY-LICENSES.html; other deployments use SITE_HOST.
    Website/chat links share page; local preview uses local main-site origin.
    cgit Source/Raw expose generated file; chat /THIRD-PARTY-LICENSES.html redirects
    to canonical page. Source exports retain offline license texts.
    Homepage /message and /message/ redirect to current chat origin.