Skip to content
infra Artifact [8fccb6390e]
Login

Artifact [8fccb6390e]

Artifact 8fccb6390ec3589feea20810a732b2488b218e701288b0409e8b305fa7f173f2:

Wiki page [NATS Server — Setup & Architecture] by dmestas on 2026-03-15 20:27:27.
D 2026-03-15T20:27:27.551
L NATS\sServer\s—\sSetup\s&\sArchitecture
N text/x-markdown
P ca9cc569520239ac7f73ae44224891038f2095cb0ac5d987dbc78e0d6814ddd1
U dmestas
W 6495
# NATS Server — Setup & Architecture

NATS 2.10 JetStream server for the **Fossil NATS Sync** project. Provides durable
pub/sub messaging between Fossil repositories and their leaf-node agents.

Public endpoint: `tls://sync.craftdesign.group:4222`

---

## Architecture

``` pikchr
    right

    # External clients
    C1: box "Leaf Node" color white fill 0x3a5a3a width 1.2 height 0.4 rad 4px
    move 0.1
    arrow "TLS :4222" above color 0x555555 thick
    move 0.1
    NS: box "nats-server" color white fill 0x2a4a8a width 1.4 height 0.4 rad 4px
    move 0.1
    arrow color 0x555555
    move 0.1
    AC: box "Account JWT" fill 0xddeeff width 1.4 height 0.4 rad 4px
    move 0.1
    arrow color 0x555555
    move 0.1
    JS: box "JetStream" color white fill 0x2a6b8a width 1.3 height 0.4 rad 4px

    # Second client below
    C2: box "Admin CLI" color white fill 0x3a5a3a width 1.2 height 0.4 rad 4px \
        at C1.s + (0, -0.7)
    arrow from C2.e right 0.1 then to NS.sw color 0x555555

    # TLS cert path below nats-server
    down
    TLS: box "acme.sh" fill 0xd0f0d0 width 1.0 height 0.35 rad 4px at NS.s + (0, -0.7)
    arrow from TLS.n to NS.s color 0x888888 <- "cert" rjust small
    CF: box "Cloudflare DNS" fill 0xd0f0d0 width 1.4 height 0.35 rad 4px at TLS.e + (0.6, 0)
    arrow <-> from TLS.e to CF.w color 0x888888 "DNS-01" above small

    # Streams below JetStream
    ES: box "EDGESYNC" fill 0xddeeff width 1.3 height 0.35 rad 4px at JS.s + (0, -0.6)
    GL: box "GO_LIBFOSSIL" fill 0xddeeff width 1.3 height 0.35 rad 4px at JS.s + (0, -1.1)
    arrow from JS.s to ES.n color 0x888888
    arrow from JS.s down 0.05 then to GL.n color 0x888888
```

---

## What Is Installed

| Component | Version | Path |
|-----------|---------|------|
| nats-server | 2.10.22 | `/usr/local/bin/nats-server` |
| nats CLI | 0.1.4 | `/usr/local/bin/nats` |
| nsc | 2.9.0 | `~/.local/bin/nsc` (dmestas) |
| acme.sh | latest | `/root/.acme.sh/acme.sh` |
| TLS cert | Let's Encrypt ECC | `/var/nats/tls/sync.craftdesign.group.{crt,key}` |

### Key Paths

| Path | Purpose |
|------|---------|
| `/usr/local/bin/nats-server` | Server binary |
| `/etc/nats/nats-server.conf` | Live rendered config |
| `/etc/nats/nats-server.conf.template` | Config template (envsubst vars) |
| `/etc/nats/operator.env` | Operator JWT (root-owned, 0600) |
| `/etc/nats/cf-token.env` | Cloudflare API token (root-owned, 0600) |
| `/var/nats/jetstream/` | JetStream message storage |
| `/var/nats/jwt/` | Account JWT resolver directory |
| `/var/nats/tls/` | TLS cert + key |
| `~/Projects/infra/nats/creds/` | `.creds` files for each user |
| `~/.local/share/nats/nsc/` | nsc operator/account/key store (dmestas) |
| `/etc/systemd/system/nats.service` | Systemd unit |

---

## Auth Hierarchy

``` pikchr
    down

    OP: box "Operator: fossil-sync" fill 0xfff0cc width 2.4 height 0.45 rad 5px
    move 0.3

    # Three accounts side by side
    SYS: box "SYS" fill 0xd0eeff width 0.9 height 0.4 rad 4px at OP.s + (-1.5, -0.5)
    ES:  box "edgesync" fill 0xd0f0d0 width 1.1 height 0.4 rad 4px at OP.s + (0, -0.5)
    GL:  box "go-libfossil" fill 0xd0f0d0 width 1.3 height 0.4 rad 4px at OP.s + (1.6, -0.5)

    line from OP.s to SYS.n color 0x888888
    line from OP.s to ES.n  color 0x888888
    line from OP.s to GL.n  color 0x888888

    # Users under edgesync
    ESA: box "admin" fill 0xeeffee width 0.9 height 0.35 rad 4px at ES.s + (-0.55, -0.55)
    ESB: box "bridge" fill 0xeeffee width 0.9 height 0.35 rad 4px at ES.s + (0.55, -0.55)
    line from ES.s to ESA.n color 0x888888
    line from ES.s to ESB.n color 0x888888

    # Users under go-libfossil
    GLA: box "admin" fill 0xeeffee width 0.9 height 0.35 rad 4px at GL.s + (-0.55, -0.55)
    GLB: box "bridge" fill 0xeeffee width 0.9 height 0.35 rad 4px at GL.s + (0.55, -0.55)
    line from GL.s to GLA.n color 0x888888
    line from GL.s to GLB.n color 0x888888

    # Annotations
    text "pub/sub: >" small at ESA.s + (0, -0.2)
    text "fossil.edgesync.>" small at ESB.s + (0, -0.2)
    text "pub/sub: >" small at GLA.s + (0, -0.2)
    text "fossil.go-libfossil.>" small at GLB.s + (0, -0.2)
```

- **admin** users: full `>` pub/sub (local provisioning only)
- **bridge** users: scoped to `fossil.<account>.>` (bridge daemon)
- **SYS** account: NATS system events (server-internal use)

---

## JetStream Streams

| Stream | Subject filter | Retention | Max size |
|--------|---------------|-----------|---------|
| `EDGESYNC` | `fossil.edgesync.>` | 7 days | 1 GB |
| `GO_LIBFOSSIL` | `fossil.go-libfossil.>` | 7 days | 1 GB |

Storage: file (`/var/nats/jetstream/`)
Replicas: 1 (single-node)
Discard policy: old (drop oldest when full)
Dupe window: 2 minutes

---

## Install Scripts (4 Phases)

All scripts live in `nats/` in the `infra` Fossil repo.

``` pikchr
    down

    P1: box "Phase 1: install.sh  (root)" fill 0xfff0cc width 3.2 height 0.45 rad 5px
    text "binary · nats user · dirs · TLS cert · systemd unit" small at P1.s + (0, -0.18)
    arrow from P1.s down 0.28 color 0x888888
    P2: box "Phase 2: bootstrap-creds.sh  (dmestas)" fill 0xd0f0d0 width 3.2 height 0.45 rad 5px
    text "nsc operator · accounts · users · .creds files · operator.env" small at P2.s + (0, -0.18)
    arrow from P2.s down 0.28 color 0x888888
    P3: box "Phase 3: start.sh  (root)" fill 0xd0eeff width 3.2 height 0.45 rad 5px
    text "render config · start service · copy account JWTs" small at P3.s + (0, -0.18)
    arrow from P3.s down 0.28 color 0x888888
    P4: box "Phase 4: provision-streams.sh  (dmestas)" fill 0xd0f0ff width 3.2 height 0.45 rad 5px
    text "create EDGESYNC and GO_LIBFOSSIL streams" small at P4.s + (0, -0.18)
```

### Running a fresh install

```sh
# On the server, as dmestas:
cd ~/Projects/infra

# Phase 1 (root) — idempotent
export NATS_VERSION=2.10.22
sudo --preserve-env=NATS_VERSION bash nats/install.sh

# Phase 2 (dmestas) — idempotent
bash nats/bootstrap-creds.sh

# Phase 3 (root) — idempotent
sudo bash nats/start.sh

# Phase 4 (dmestas)
bash nats/provision-streams.sh
```

**Prerequisites for Phase 1:**
- `/etc/nats/cf-token.env` must exist with `CF_Token="<token>"` and mode 0600

**Prerequisites for Phase 2:**
- nsc installed (`~/.local/bin/nsc`)
- Phase 1 complete

---

## Firewall

UFW is used (not Hetzner Cloud firewall).

```
sudo ufw allow 4222/tcp   # NATS TLS (already open)
sudo ufw status
```

Port 4222 is the only NATS-related port that needs to be open externally.

Z d2cb1ed55dd293358df6c8502956281a