csaf-crud 1.3.7

CSAF 2.0 / 2.1 advisory CRUD server with HATEOAS JSON API and HTML UI (TLS 1.3, HTTP/1.1 + HTTP/2 + HTTP/3)
<!-- SPDX-License-Identifier: Apache-2.0 -->
<!-- SPDX-FileCopyrightText: 2026 ndaal GmbH & Co KG, Cologne -->

# Administrator Guide

Deployment, configuration, operations, and recovery for ndaal
CSAF-CRUD. For exhaustive key/flag descriptions see
[REFERENCE.md](../REFERENCE.md); for end-user workflows see the
[User Guide](User_Guide.md); for failure diagnosis see the
[Troubleshooting Guide](Troubleshooting_Guide.md).

## Overview

ndaal CSAF-CRUD is a single self-contained binary. It embeds two
databases (redb for CSAF documents, bundled SQLite for the audit log
and the user schema), generates its own TLS 1.3 certificate, and
needs no external services.

Two binaries ship from the workspace:

- `csaf-crud` — the web UI and JSON API server.
- `ndaal-csaf-cli` — a headless CLI for import, export, validate,
  and stats.

## Platform support

- Minimum Rust toolchain: `1.93.0`, edition `2024`.
- Packaged targets: `x86_64-unknown-linux-gnu`,
  `aarch64-unknown-linux-gnu`, `x86_64-apple-darwin`,
  `aarch64-apple-darwin`, `aarch64-pc-windows-msvc`.
- Cross-compilation uses cargo-zigbuild.

## Installation

Build from source in the workspace root:

```bash
cargo build --release
# binaries: target/release/csaf-crud and target/release/ndaal-csaf-cli
```

To build without the optional OASIS validator and QUIC features
(smaller binary, fewer dependencies):

```bash
cargo build --release --no-default-features
```

## Configuration

The server reads a TOML file at startup. The path is the first
command-line argument and defaults to `config/default.toml`.

```bash
csaf-crud /etc/csaf-crud/config.toml
```

The shipped `config/default.toml`:

```toml
listen_addr = "127.0.0.1"
listen_port = 8180
data_dir    = "./data"
# tls_cert = "config/cert.pem"
# tls_key  = "config/key.pem"
```

Keys (full descriptions and the compiled-in fallbacks are in
[REFERENCE.md](../REFERENCE.md#configuration)):

- `listen_addr` — bind address. Default `127.0.0.1`. Set to `::` or
  a routable address to expose the server beyond loopback (read the
  security note below first).
- `listen_port` — TCP port for HTTP/1.1 and HTTP/2. The bundled
  config uses `8180`; the QUIC/HTTP3 listener binds `listen_port + 1`
  (UDP, `8181`).
- `data_dir` — directory holding `csaf.redb` and `csaf.sqlite`.
  Created on first run if absent.
- `tls_cert` / `tls_key` — optional PEM paths. When unset, the
  server generates a self-signed certificate at startup.

Operational settings (export directories, sidecar toggles,
publisher defaults, classification policy, CSAF mode) are **not** in
the TOML file. They live in redb and are edited at runtime through
the `/settings` page or `PUT /api/v1/settings`, and reset with
`POST /settings/reset`. See [REFERENCE.md](../REFERENCE.md#runtime-settings).

## TLS

- Transport is **TLS 1.3 only** (no TLS 1.2 fallback), via rustls
  with the ring crypto provider — no OpenSSL or aws-lc.
- When no certificate is configured, the server issues a self-signed
  certificate valid for **45 days** with SANs for `localhost`,
  `127.0.0.1`, and `::1`. It is regenerated on every restart.
- For a stable certificate, set `tls_cert` and `tls_key` to PEM
  files. Rotate them before expiry.
- Verify the deployed endpoints with the bundled harness
  `test/testssl/run.sh`.

## Authentication and access control

This release enforces **no** authentication or authorization. Every
web page and API route is reachable by anyone who can connect. The
`users` and `sessions` tables exist in SQLite (Argon2id hashing,
per-user API keys) but are not yet enforced by the router.

Until authentication lands, operate the service accordingly:

- Keep `listen_addr = "127.0.0.1"` and reach it through an
  authenticating reverse proxy or an SSH tunnel.
- Never expose it directly to an untrusted network.
- Restrict filesystem access to `data_dir` and the export/dump
  directories — they hold every advisory and the audit log.

## Data directories

Created on first run relative to the working directory unless
reconfigured in settings:

| Directory | Default | Purpose |
| --- | --- | --- |
| `data/` | `data_dir` | `csaf.redb` + `csaf.sqlite` |
| `data_import/` | `import_directory` | Inbox scanned by import |
| `data_export/` | `export_directory` | Exported CSAF + sidecars |
| `data_dump/` | `dump_directory` | Database snapshots |
| `data_log/` | `log_directory` | Rolling tracing logs |

## Running as a service

Example systemd unit (Linux):

```ini
[Unit]
Description=ndaal CSAF-CRUD
After=network.target

[Service]
ExecStart=/usr/local/bin/csaf-crud /etc/csaf-crud/config.toml
WorkingDirectory=/var/lib/csaf-crud
User=csaf
Environment=RUST_LOG=info
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

Place the data directories under `WorkingDirectory` (or set absolute
paths in settings) so the service user owns them.

## Logging

- Logs are written to stderr and to a daily-rotated file
  `<log_directory>/csaf-crud.log.<YYYY-MM-DD>`.
- The level is controlled by `RUST_LOG` (default `info`). Use
  `RUST_LOG=debug` for verbose diagnosis; library noise from
  `hyper`, `quinn`, and `h2` is filtered at `info`.
- If the log directory cannot be created, the server logs a warning
  to stderr and continues with stderr-only logging.

## Audit log

Every create, update, delete, import, export, dump, and
settings-reset is recorded in the SQLite `audit_log` table. Export it
from `/admin/export` → Export Audit Log, or
`POST /api/v1/.../export-audit`, in Markdown, CSV, JSON, or
SARIF 2.1.0. Read it via `GET /api/v1/audit-log`.

## Backup and recovery

### Backup

Trigger a consistent snapshot from `/admin` → Dump Database (or
`POST /admin/dump`). It writes timestamped copies of both databases
plus the enabled hash sidecars to `dump_directory`:

```text
csaf.redb.<timestamp>      + .sha-256 / .sha-512 / .sha3-512 / …
csaf.sqlite.<timestamp>    + .sha-256 / .sha-512 / .sha3-512 / …
```

The redb snapshot is taken under a read transaction; the SQLite
snapshot uses the online backup API — both are consistent against
live traffic. Copy the `dump_directory` contents to off-host
storage on a schedule.

### Recovery

1. Stop the server.
2. Verify the snapshot integrity against its sidecar, for example
   `shasum -a 512 -c csaf.sqlite.<timestamp>.sha-512`.
3. Copy `csaf.redb.<timestamp>` to `<data_dir>/csaf.redb` and
   `csaf.sqlite.<timestamp>` to `<data_dir>/csaf.sqlite`. Remove any
   stale `csaf.sqlite-wal` / `csaf.sqlite-shm` files.
4. Start the server; it opens the restored databases normally.

## Upgrades

1. Back up (dump) the databases first.
2. Replace the binary and restart.
3. SQLite migrations run automatically on open. redb and SQLite
   schemas are forward-compatible within a minor series.

## Verification

After deployment, confirm the service is healthy:

```bash
curl -k https://127.0.0.1:8180/api/v1/system/health
# {"data":{"status":"healthy", ...}, "_links": {...}}
```

`-k` is required while the self-signed certificate is in use.