Files
aia-website/www/integrations/nixos/deploy.md
T

136 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Contabo release operations
This site publishes static content through the merged Contabo release manager.
It does not run `nixos-rebuild`, reload nginx, change DNS, or write directly to
`/srv/www/azinstitute4autism.com/www/dist`.
## Required local tools and private inputs
- Node.js 22+, Git, tar, SSH and rsync.
- Either an OpenSSH host alias already configured locally, or a normal SSH
identity accepted by the existing root receiver.
- For environment-only invocations: `AIA_DEPLOY_HOST=administrator@host`,
`AIA_DEPLOY_SSH_IDENTITY=/absolute/path/to/identity`, and
`AIA_PORTAL_PRODUCTION_CIPHERTEXT_FILE=/absolute/private/employee-portal/approved-production-ciphertext.json`.
- For positional invocations, use either `SSH_HOST CIPHERTEXT` (OpenSSH resolves
the host alias and identity) or `SSH_HOST SSH_IDENTITY CIPHERTEXT` (the CLI
supplies `-i` and `IdentitiesOnly=yes`).
The payload remains outside Git and the source snapshot. It is browser ciphertext
only; neither access code nor decrypted portal HTML enters an Astro environment
variable, public resource directory, build log, or deployment manifest.
## Commands
From `www/`:
```sh
npm run deploy -- --dry-run
# Use a configured OpenSSH host alias and a private ciphertext path.
npm run deploy -- --dry-run aiar \
"$PWD/private/employee-portal/approved-production-ciphertext.json"
# Supply an explicit SSH identity; paths may contain spaces.
npm run deploy -- --dry-run 'root@66.94.121.180' \
"$HOME/.ssh/AIAMarketing" \
"$PWD/private/employee-portal/approved-production-ciphertext.json"
```
Dry-run performs the local production build, tests, artifact verification,
manifest creation and source-snapshot preparation. With no private production
payload it uses a generated synthetic two-stream fixture and explicitly reports
that the result is not production-ready. It never opens SSH or rsync.
```sh
AIA_DEPLOY_HOST='root@approved-host' \
AIA_DEPLOY_SSH_IDENTITY="$HOME/.ssh/AIAMarketing" \
AIA_PORTAL_PRODUCTION_CIPHERTEXT_FILE="$PWD/private/employee-portal/approved-production-ciphertext.json" \
npm run deploy -- --confirm
```
The equivalent confirmed positional commands are:
```sh
npm run deploy -- --confirm aiar \
"$PWD/private/employee-portal/approved-production-ciphertext.json"
npm run deploy -- --confirm 'root@66.94.121.180' \
"$HOME/.ssh/AIAMarketing" \
"$PWD/private/employee-portal/approved-production-ciphertext.json"
```
When positional values are present, they take precedence over all three
deployment environment variables. Two positional values deliberately omit
`-i`, allowing the local OpenSSH configuration to select the identity. Dry-run
never opens SSH or rsync, even when host and identity arguments are provided.
Invalid modes, mixed confirmation flags, incomplete positional sets, unsafe
hosts, relative positional paths, and missing ciphertext files fail before a
release can be promoted.
The confirmed command builds with `PUBLIC_ALLOW_INDEXING=true`, Form A live and
the approved live counters configuration, then uploads only fresh commit-specific `staging/source-<sha>` and
`staging/dist-<sha>` directories. It runs, in order:
```text
aia-website-release verify
aia-website-release sync-source
aia-website-release promote
```
The VPS verifies the manifest, promotes an immutable release by atomic symlink,
and retains the prior release. A successful rsync transfer alone is not success.
Rollback requires the reviewed manifest retained at the server’s fixed path:
```sh
AIA_DEPLOY_HOST='root@approved-host' \
AIA_DEPLOY_SSH_IDENTITY="$HOME/.ssh/AIAMarketing" \
npm run deploy:rollback -- FULL_APPROVED_SHA --confirm
```
## Employee resources: separate one-time operation
Approved non-PHI employee files are never part of an ordinary release. After
the VPS SOPS-backed Basic Auth material is independently ready:
```sh
npm run deploy:portal-resources -- --dry-run
AIA_DEPLOY_HOST='root@approved-host' \
AIA_DEPLOY_SSH_IDENTITY="$HOME/.ssh/AIAMarketing" \
npm run deploy:portal-resources -- --confirm
```
For the first install, the client extracts `portal-resource-install.sh` from the
current reviewed Git commit, uploads it into the root-controlled commit staging
directory, verifies its SHA-256 remotely, and invokes it through NixOS's base
`/bin/sh` and coreutils path. A prior public site release and a globally installed
Node.js runtime are not prerequisites. It reads only the private collection manifest,
accepts approved `STAFF-INTERNAL` and `MANAGEMENT-INTERNAL` mappings to
`/staff-resources/` and `/management-resources/`, verifies every local hash, rejects links/traversal and
uploads a root-controlled temporary tree. It never uses `--delete` for resource
directories and refuses to replace a different existing file. The VPS installs
regular files as `root:nginx` mode `0640` and reports metadata only.
Local mapping approval authorizes preparation and installation only. After the
authorized VPS rehearsal, verify installed hashes, ownership/mode, staff versus
management Basic Auth, and both protected URLs before deploying portal links.
Before public DNS cutover, test the site with approved host-header/SNI methods,
including the portal’s noindex response, valid ciphertext unlock, both protected
resource namespaces, denial cases, redirects, Form A, IPv4 and IPv6. Portal
shared-code rotation is coordinated: update VPS SOPS inputs and privately
re-encrypt the browser payload together. This interim portal is non-PHI and is
not an employee identity system.
The portal uses one code entry per page session. A successful decrypt retains
the code only in page memory and sends the matching fixed username (`staff` or
`management`) in an explicit same-origin Basic Authorization header when a
protected link is clicked. It downloads the authenticated response as a Blob
and never navigates directly to the protected URL after an error. Verify that
ordinary clicks download without a native credential dialog, inline errors
appear for denied or unavailable files, direct unauthenticated requests remain
401, and neither role can request the other namespace. Reloading, leaving the
page, or an Astro page swap discards the in-memory credential.