# TeXise deployment foundation

TeXise production is designed for `https://www.texise.com` behind Apache2. The
browser application is static; Apache proxies only trusted API routes to a
small Node service bound to `127.0.0.1:3100`.

## Filesystem layout

The production source tree is installed directly at:

```text
/var/www/texise.com/public_html/
```

The important paths are:

- `/var/www/texise.com/public_html` — complete TeXise source tree.
- `/var/www/texise.com/public_html/dist` — active browser build served by Apache.
- `/var/www/texise.com/public_html/texise-media` — intentionally public,
  immutable CMS media; Blog covers live under `texise-media/blog`.
- `/var/www/texise.com/public_html/texise-data` — private trusted-service
  runtime/data root. Apache explicitly denies `/texise-data/*`.
- `/var/www/texise.com/public_html/dist.previous` — previous browser build,
  retained by the root build pipeline for deployment rollback.
- `/var/www/texise.com/public_html/server` — trusted Node API source staged by the installer.
- `/var/www/texise.com/public_html/texise-data/runtime/current/server` — active trusted API runtime snapshot used by `texise.service`.
- `/var/www/texise.com/public_html/texise-data/runtime/previous` — previous API runtime retained for paired rollback.
- `/etc/texise` — configuration, predefined licence hashes, and entitlement
  signing keys. Install/upgrade preserve this directory.
- `/var/www/texise.com/public_html/texise-data/audit` — writable trusted audit state.
- `/var/www/texise.com/public_html/texise-data/cache` — writable server/compiler caches.
- `/var/www/texise.com/public_html/texise-data/tmp` — writable trusted temporary state.
- `/var/www/texise.com/public_html/texise-data/log` — service-specific logs/data.

There is no R13 `/opt/texise` API release tree and no
`/var/www/app.texise.com/releases` frontend tree. `public_html` is never replaced
with a symlink.

The browser remains authoritative for user project storage, Local Compile, PDF rendering, TeX Live caches, browser Git, Free local-AI model/runtime storage and inference, browser-local Project Meaning embeddings, local Project RAG evidence/generation, and optional Pro cloud BYOK AI requests. Cloud Compile · Pro and Linux Terminal · Pro are explicit server execution paths and do not change local project ownership. Local generation/embedding model-runtime assets are fetched only after explicit user action and are stored outside project folders; Project Meaning vectors are stored in a project-scoped browser-local IndexedDB namespace rather than NativeFS or project ZIPs.

## Prepare the production source tree

Extract or clone the complete repository so `package.json` is exactly here:

```text
/var/www/texise.com/public_html/package.json
```

For example, after placing the source archive on the host:

```bash
sudo mkdir -p /var/www/texise.com/public_html
sudo unzip texise-source.zip -d /var/www/texise.com/public_html
cd /var/www/texise.com/public_html
```

If the archive contains an additional top-level directory, move its contents so
the repository root itself is `/var/www/texise.com/public_html` before
running the installer.

The installer intentionally refuses to deploy from another source path. It also
refuses a symlinked `public_html`, which prevents an old R12 release symlink from
being mistaken for the R13 source tree. If upgrading an R12 host, remove or move
the old `public_html` symlink first, create a real `public_html` directory, and
place the R13 source there. The old release directories can be retained until
the new installation is verified. This keeps the Apache, systemd, build,
upgrade, and operator paths deterministic.

## Install

Run from the production repository root on the target Ubuntu/Apache host. The
host needs Node.js 22+ and either PNPM or Corepack capable of providing the
repository-pinned PNPM version:

```bash
cd /var/www/texise.com/public_html
sudo ./scripts/install.sh
```

The installer:

1. verifies the source is `/var/www/texise.com/public_html`;
2. creates dedicated `texise`, `texise-compile` and `texise-console` service accounts; only the two execution accounts receive Docker supplemental-group access;
3. creates trusted config/runtime directories and makes the source API path readable by the unprivileged service;
4. installs `/etc/texise/texise.env` and `config.json` only when absent;
5. creates `/etc/texise/license-keys`;
6. generates the Ed25519 entitlement signing keypair and stateless refresh
   encryption key only when absent;
7. runs `pnpm install --frozen-lockfile`;
8. runs the TeXise static contract audit and prepares optional browser compiler assets;
9. runs `pnpm run build`, generating release identity, SPDX SBOM, provenance and SHA-256 inventory;
10. verifies and marks the root production `dist` read-only;
11. stages a paired trusted-API runtime snapshot under `/var/www/texise.com/public_html/texise-data/runtime/current`;
12. installs the `texise-license` administration command;
13. installs/enables `texise.service`, `texise-latex-compile.service` and `texise-console-gateway.service`;
14. installs the Apache vhost with
    `DocumentRoot /var/www/texise.com/public_html/dist`;
15. enables required Apache modules (including WebSocket proxying) and validates the configuration with `apache2ctl configtest`;
16. restarts the trusted API and both execution services, reloads Apache, certifies API/frontend/PWA/CSP/signaling plus enabled Cloud Compile/Linux Terminal capabilities, verifies redirects/media, and confirms frontend/API release IDs match before disabling obsolete Hocuspocus/Reverb services.

The canonical certificate is discovered/validated for both `texise.com` and `www.texise.com`. The existing `app.texise.com` certificate is discovered independently for the redirect-only compatibility vhost; certificate paths are never guessed from the primary certificate.

## Production build contract

The repository deliberately retains Vite's native browser build at:

```text
packages/tooling/browser-entry/dist
```

because Cloudflare and desktop tooling still consume it.

The root command:

```bash
pnpm run build
```

performs two stages:

1. builds `packages/tooling/browser-entry/dist`;
2. stages and validates that build in root `dist.next`, preserves the current root `dist` as `dist.previous`, then activates the new root `dist`;
3. writes `release.json`, `release/sbom.spdx.json`, `release/provenance.json` and `release/SHA256SUMS`.

Therefore the Apache-served production artifact is always:

```text
/var/www/texise.com/public_html/dist
```

A failed Vite build never touches the currently served root `dist`. A failure
while staging/validating the copied build also leaves the current `dist`
unchanged. Post-deployment health failure restores both `dist.previous` and the previous trusted-API runtime snapshot when available. The installer refuses to accept a deployment when `/api/health` and `/release.json` advertise different release IDs.

## Apache architecture

Apache serves only static browser files from:

```text
/var/www/texise.com/public_html/dist
```

Requests under `/api/*` are reverse-proxied to:

```text
http://127.0.0.1:3100/api/
```

Production collaboration is same-origin. The authoritative Yjs document + awareness relay is:

```text
wss://www.texise.com/collab-live
        → ws://127.0.0.1:3101/collab-live
```

`/collab-signal` remains temporarily available only for migration compatibility with older y-webrtc clients. Current TeXise clients synchronize manuscript Yjs updates and awareness through `/collab-live`; the trusted relay retains bounded opaque room state in memory for reconnect tolerance and does not persist manuscript files.

Linux Terminal · Pro is also same-origin, but only its authenticated websocket surface is proxied:

```text
wss://www.texise.com/console-gateway/console/<session-id>
        → ws://127.0.0.1:8787/console/<session-id>
```

The short-lived terminal credential is carried in `Sec-WebSocket-Protocol`, not the URL. Gateway `/internal/*` HTTP APIs remain loopback/private and are called only by `texise.service`. Obsolete `/collab-ws`, `/app/*` and `/apps/*` routes are not exposed by the canonical vhost.

Unknown non-file routes are rewritten to `/index.html` so browser-side SPA
routing continues to work. The source tree above `dist`, including
`package.json`, `server/`, scripts, repository metadata, and source packages, is
not inside Apache's DocumentRoot.

The vhost keeps `.wasm` MIME configuration, security headers, non-cacheable
`index.html`/manifest/service-worker responses, and immutable caching for
versioned static asset groups.

## Pro execution services

The execution services use separate images and policies: Cloud Compile uses `TEXISE_CLOUD_COMPILE_IMAGE=texlive/texlive:latest`; Linux Terminal Pro uses `TEXISE_CONSOLE_IMAGE=texlive/texlive-console:pro`. The default Console image is a TeXise-local build produced from `deploy/docker/console/`, not a Docker Hub pull; custom non-default Console images remain operator-managed external images.

`texise-latex-compile.service` runs as dedicated `texise-compile`, listens on `127.0.0.1:3001`, accepts only the private `X-Compile-Secret`, and launches non-interactive/no-network Docker jobs with no shell escape and bounded resources. Active jobs live under `public_html/texise-data/tmp/cloud-compile`.

`texise-console-gateway.service` runs as dedicated `texise-console`, listens on `127.0.0.1:8787`, and launches interactive Pro sessions with a per-session writable workspace under `public_html/texise-data/tmp/console`. The terminal container has controlled bridge egress for GitHub/Python/scientific workflows but no Docker socket, host filesystem mount or TeXise secret mount. Workspace path confinement, quotas, idle/session expiry and cleanup are enforced by the gateway.

`texise-collab.service` and `texise-reverb.service` are retained only as rollback units and are disabled after the modern health checks succeed.

## Trusted API service

`texise.service` runs from the rollback-capable runtime snapshot:

```text
WorkingDirectory=/var/www/texise.com/public_html/texise-data/runtime/current/server
ExecStart=/usr/bin/node /var/www/texise.com/public_html/texise-data/runtime/current/server/index.mjs
```

`release.env` in the same runtime snapshot pins the API release ID. The runtime snapshot links only to the installed dependency tree and is made read-only after activation.

It runs as the unprivileged `texise` service user. Trusted configuration remains
read-only under `/etc/texise`. The service runtime snapshot under
`public_html/texise-data/runtime/current` is also read-only; write access is
restricted to `public_html/texise-data/{audit,cache,tmp,log}` and
`public_html/texise-media/blog`. Apache serves `/texise-media/*` as static media
and explicitly denies `/texise-data/*`.

## Configuration

Trusted configuration has two layers, in increasing precedence:

1. `/etc/texise/config.json` for non-secret product switches;
2. `/etc/texise/texise.env` / process environment for deployment values and
   secrets.

`TEXISE_CONFIG_FILE` can point the trusted service at another JSON config file.
Environment values such as `TEXISE_PUBLIC_URL` override matching JSON values.

Do not put Payhip secrets or the entitlement private key in Vite environment
variables or the static web root.

The Node service exposes:

- `GET /api/health`
- `GET /api/license/public-key`
- `POST /api/license/activate`
- `POST /api/license/refresh`

`/api/license/activate` checks `/etc/texise/license-keys` first and then, when
configured, Payhip's v2 licence verification API. `PAYHIP_PRODUCT_SECRET_KEY`
stays only in `/etc/texise/texise.env`; it is never included in the browser
bundle. `PAYHIP_PRODUCT_LINK` may be set as an additional response check using Payhip's short `product_link` token (for example `mVT0`) or a normal Payhip `/b/<token>` product URL. `TEXISE_SHOP` remains the public checkout destination and is never compared as a full URL against Payhip's short API token.

Successful activation returns an Ed25519-signed browser entitlement. The raw
Payhip licence key is not stored inside that entitlement. Instead the server
places the refresh material inside an AES-256-GCM encrypted handle using
`/etc/texise/entitlement-refresh.key`, so periodic revalidation remains
stateless and does not require a licence database.

The default entitlement asks for an online refresh after 30 days and continues
to verify locally for a further 90-day offline grace period. Configure these
with `TEXISE_LICENSE_REFRESH_DAYS` and `TEXISE_LICENSE_OFFLINE_GRACE_DAYS`.

## Predefined licence administration

The installer places `texise-license` at `/usr/local/bin/texise-license`.

```bash
sudo texise-license add TEXISE-PRO-XXXX-XXXX
sudo texise-license remove TEXISE-PRO-XXXX-XXXX
texise-license list
```

Only SHA-256 hashes are stored in `/etc/texise/license-keys`; `list` displays
short fingerprints rather than licence values.

## Release-quality matrix

Before tagging a public release, run:

```bash
corepack enable
pnpm run release:quality
```

R14.21 first runs dependency-free archive hardening tests and `release-runtime-preflight.json`, then performs a frozen install, lint/type checks, unit tests, full BusyTeX compiler provisioning, production build, the TeXise static contract audit, Playwright browser installation, the complete production-preview E2E suite in branded Microsoft Edge, Chromium, and Firefox, plus the Chromium-ZIP-to-Firefox portability gate. A machine-readable `release-quality-report.json` records every step. Only after all runtime/browser gates pass does TeXise emit `dist/release/certification.json` and refresh `dist/release/SHA256SUMS`.

Normal `TEXISE_COMPILER_PROFILE=full` install/upgrade no longer reruns this complete CI/browser matrix. It performs the release lint/type gates, provisions and verifies the full compiler profile, runs the static contract audit, builds the production browser artifact, and performs deployment health checks. The first install stores the versioned BusyTeX runtime under `/var/www/texise.com/public_html/texise-data/cache/compiler`; later upgrades using the same BusyTeX version restore it from that persistent cache instead of downloading/extracting the 524 MB asset bundle again. The full profile keeps SwiftLaTeX as the preferred pdfLaTeX runtime for faster ordinary compiles; BusyTeX remains available on demand for XeLaTeX and LuaLaTeX.

Formal browser certification remains explicit: run `pnpm run release:quality` on the release candidate, or set `TEXISE_DEPLOY_CERTIFY=1` (for example, `sudo env TEXISE_DEPLOY_CERTIFY=1 ./scripts/install.sh`) when intentionally asking the installer to perform the complete unit + Edge/Chromium/Firefox certification before activation. A deliberately reduced `pdflatex` profile can still build for constrained/offline deployments, but it does not receive the full three-engine production-browser certification. After a formally certified deployment, an external smoke can be run with:

```bash
pnpm run release:deployment-smoke
# or against another origin
TEXISE_DEPLOY_URL=https://staging.example.org pnpm run release:deployment-smoke
```

The smoke requires matching frontend/API/certification release IDs, CSP/HSTS on HTTPS, Edge+Chromium+Firefox certification, and pdfLaTeX plus BusyTeX XeLaTeX/LuaLaTeX.

The repository also contains `.github/workflows/texise-release-quality.yml` so the same matrix can run on pushes, pull requests, and manually.

## Upgrade

Update/replace the source files in place, keeping the repository root at the
production path, then run:

```bash
cd /var/www/texise.com/public_html
sudo ./scripts/upgrade.sh
```

The upgrade preserves every `/etc/texise/*` file, runs a frozen-lockfile PNPM
install, rebuilds root `dist`, refreshes the service/vhost definitions, restarts
the API, reloads Apache, and runs health checks. The previous root static build
is retained as `dist.previous` until the next successful build cycle replaces
it.

There is no release/symlink activation step in R13.

## Uninstall

```bash
cd /var/www/texise.com/public_html
sudo ./scripts/uninstall.sh
```

Normal uninstall removes the service, Apache site, administration command, and
generated `dist`/`dist.next`/`dist.previous` browser builds while preserving:

- the source repository under `/var/www/texise.com/public_html`;
- `/etc/texise` configuration and signing material;
- runtime data/log directories.

To remove trusted configuration, runtime data, logs, and signing material too:

```bash
sudo ./scripts/uninstall.sh --purge
```

Even `--purge` does **not** delete the source tree. Delete source code manually
only when that is explicitly intended.

## R13 release hardening

Before activating a production release, run:

```bash
node scripts/texise-release-audit.mjs
```

The audit checks TeXise identity, the root-dist build contract, Apache's exact
`public_html/dist` DocumentRoot, systemd source paths, absence of legacy R12
release trees, source-safe uninstall behavior, security headers, loopback
trusted-API configuration, the blank Payhip secret example, PWA/offline worker
registration, and the minimum Free/Pro template catalogue. It supplements
rather than replaces the repository lint/test/build requirements.

The root-scoped `texise-texlive-sw.js` caches the application shell and
same-origin immutable browser assets. After a successful online visit TeXise
can reload its UI offline; project content remains in the user's selected
native folder or browser storage and is not copied into the service-worker
cache.


### Linux Terminal scientific Python certification

Linux Terminal Pro production certification is a hard certificate for the dedicated `texlive/texlive-console:pro` image: bash, Git/GitHub CLI, Python 3 with pip/venv, NumPy, SciPy, pandas, Matplotlib, SymPy, GCC/G++, make/cmake, archive/HTTP utilities, TeX Live engines, Biber, latexmk, websocket execution and authenticated upload/download.

Scientific Python packages are part of the dedicated `texlive/texlive-console:pro` image itself; TeXise no longer provisions or mounts a host-side Python overlay.
