105 lines
4.7 KiB
Markdown
105 lines
4.7 KiB
Markdown
# 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.
|
||
- A normal SSH identity accepted by the existing root receiver:
|
||
`AIA_DEPLOY_SSH_IDENTITY=/absolute/path/to/identity`.
|
||
- `AIA_DEPLOY_HOST=administrator@host` (or the approved equivalent).
|
||
- For a real public release, an owner-controlled, regular encrypted payload:
|
||
`AIA_PORTAL_PRODUCTION_CIPHERTEXT_FILE=/absolute/private/employee-portal/approved-production-ciphertext.json`.
|
||
|
||
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
|
||
```
|
||
|
||
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 confirmed command builds with `PUBLIC_ALLOW_INDEXING=true`, Form A live and
|
||
counters off, 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.
|