Skip to content

Commit bff30d1

Browse files
committed
feat(web): motion + Getting Started / Configuration docs
- Add two entry-point docs the site was missing: GETTING_STARTED.md (install → profile → backup → verify → restore → sync, with the exit- code taxonomy) and CONFIGURATION.md (the full config-file schema: defaults, profiles, secret refs, storage, retention, audit, telemetry, secrets, groups). Both are grounded in the README + config structs and flow into the site via the existing docs pipeline (nav updated; they lead the sidebar). - Add deliberate, on-thesis motion: a staggered hero load-in (rise) and scroll-revealed feature cards (Reveal), reinforcing the top-to-bottom "flow" metaphor — not scattered effects. The existing flow-line stays the signature. - Reveal is a progressive enhancement: SSR renders content visible, and JS only then arms the hidden→reveal behavior, so no-JS and reduced-motion users never get invisible content (verified: feature text present in SSR HTML; reduced-motion screenshot shows full resting state). npm build + lint green; 9 docs pages prerender.
1 parent fb73579 commit bff30d1

6 files changed

Lines changed: 391 additions & 11 deletions

File tree

docs/CONFIGURATION.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Configuration reference
2+
3+
siphon reads one YAML config file. Find or edit it with `siphon config path` /
4+
`siphon config edit`. Location (XDG-compliant; override with `SIPHON_CONFIG_HOME`):
5+
6+
- **Linux:** `$XDG_CONFIG_HOME/siphon/config.yaml``~/.config/siphon/config.yaml`
7+
- **macOS:** `~/.config/siphon/config.yaml`
8+
- **Windows:** `%APPDATA%\siphon\config.yaml`
9+
10+
The file is safe to commit **as long as every secret is a reference**, not a
11+
literal (see [Secret references](#secret-references)).
12+
13+
## Table of contents
14+
15+
- [Top-level shape](#top-level-shape)
16+
- [defaults](#defaults)
17+
- [profiles](#profiles)
18+
- [Secret references](#secret-references)
19+
- [storage](#storage)
20+
- [retention](#retention)
21+
- [audit](#audit)
22+
- [telemetry](#telemetry)
23+
- [secrets](#secrets)
24+
- [groups](#groups)
25+
26+
## Top-level shape
27+
28+
```yaml
29+
version: 1
30+
defaults: { … } # cross-profile defaults
31+
storage: { … } # where dumps live (local | s3)
32+
audit: { … } # destructive-op audit log
33+
telemetry: { … } # opt-in aggregate metrics
34+
secrets: { … } # optional secret backends
35+
profiles: { … } # named connections
36+
groups: { … } # profile groups (gating policy)
37+
```
38+
39+
Every block except `profiles` is optional; omitted blocks use safe defaults.
40+
41+
## defaults
42+
43+
```yaml
44+
defaults:
45+
dump_dir: ~/.local/share/siphon/dumps # local catalog path (when storage is local)
46+
jobs: 4 # parallel workers where supported
47+
compression: 1 # dump compression level
48+
retention: { … } # default retention policy (see below)
49+
```
50+
51+
## profiles
52+
53+
A named connection. The map key is the profile name.
54+
55+
```yaml
56+
profiles:
57+
prod:
58+
driver: postgres # postgres | mysql | mariadb
59+
host: db.example.com
60+
port: 5432
61+
user: app_user
62+
password: env:PROD_DB_PASS # a secret reference (see below)
63+
database: app_prod
64+
sslmode: require
65+
group: critical # optional; ties to a groups: entry
66+
retention: { … } # optional per-profile override (replaces defaults)
67+
tunnel: # optional SSH bastion
68+
bastion: jump@bastion.example.com
69+
local_port: 15432 # defaults to the DB port
70+
```
71+
72+
## Secret references
73+
74+
Any secret field (today: `password`, and a group's `totp_secret`) is resolved at
75+
runtime by scheme:
76+
77+
| Scheme | Example | Source |
78+
| --- | --- | --- |
79+
| `env` | `env:PROD_DB_PASS` | environment variable |
80+
| `keychain` | `keychain://prod-db` · `keychain://svc/acct` | OS credential store |
81+
| `awssm` | `awssm://prod/db#password` | AWS Secrets Manager (a `#key` selects a JSON field) |
82+
| *(none)* | `hunter2` | literal value — **don't commit this** |
83+
84+
`keychain://` is always available; `awssm://` must be enabled under
85+
[`secrets`](#secrets). See [docs/OPS.md](OPS.md#secret-backends) for detail.
86+
87+
## storage
88+
89+
Where the dump catalog physically lives. Omitted = local at `defaults.dump_dir`.
90+
91+
```yaml
92+
storage:
93+
type: s3 # "local" (default) | "s3"
94+
bucket: my-siphon-dumps # required for s3
95+
prefix: prod # optional key prefix within the bucket
96+
region: us-east-1
97+
endpoint: "" # optional: custom endpoint for MinIO / R2
98+
```
99+
100+
S3 credentials come from the standard AWS chain, never from config. Full detail:
101+
[docs/STORAGE.md](STORAGE.md).
102+
103+
## retention
104+
105+
Drives `siphon dumps prune`. A profile's block **replaces** the defaults block
106+
wholesale. An empty/omitted policy keeps everything.
107+
108+
```yaml
109+
defaults:
110+
retention:
111+
keep_last: 7 # keep the N newest chains
112+
max_age: 720h # keep chains younger than this (Go duration)
113+
gfs: { daily: 7, weekly: 4, monthly: 6 }
114+
```
115+
116+
A chain is kept if it satisfies **any** active rule. Full detail:
117+
[docs/RETENTION.md](RETENTION.md).
118+
119+
## audit
120+
121+
Append-only JSONL log of destructive operations. Off by default.
122+
123+
```yaml
124+
audit:
125+
enabled: true
126+
path: ~/.local/state/siphon/audit.log # optional; this is the default
127+
```
128+
129+
## telemetry
130+
131+
Opt-in aggregate per-op counts and error tallies (op name + outcome only — never
132+
identifying data). Off by default.
133+
134+
```yaml
135+
telemetry:
136+
enabled: true
137+
path: ~/.local/state/siphon/telemetry.json # optional; this is the default
138+
```
139+
140+
## secrets
141+
142+
Enables optional secret backends. `keychain://` works with no config; AWS
143+
Secrets Manager is gated here because constructing it loads AWS config.
144+
145+
```yaml
146+
secrets:
147+
awssm: true # enable the awssm:// backend
148+
awssm_region: us-east-1 # optional; defaults to the AWS chain's region
149+
```
150+
151+
## groups
152+
153+
A group applies a gating policy to its member profiles before destructive ops.
154+
155+
```yaml
156+
groups:
157+
critical:
158+
confirm_destructive: true # operator must retype the profile name
159+
require_2fa: true # operator must enter a current TOTP code
160+
totp_secret: env:SIPHON_PROD_TOTP # base32 RFC-6238 secret (a secret-ref)
161+
color: red # TUI accent
162+
```
163+
164+
`require_2fa` with no resolvable `totp_secret` fails closed. Full detail:
165+
[docs/OPS.md](OPS.md#2fa--group-gating).

docs/GETTING_STARTED.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Getting started
2+
3+
This walks from install to your first backup, restore, and sync. It assumes you
4+
have the native client tools for your engine on `PATH` (siphon shells out to
5+
them — `pg_dump`/`pg_restore`, `mysqldump`/`mysql`, or `mariadb-dump`/`mariadb`).
6+
7+
## Table of contents
8+
9+
- [Install](#install)
10+
- [Register a profile](#register-a-profile)
11+
- [Back up](#back-up)
12+
- [Verify](#verify)
13+
- [Restore](#restore)
14+
- [Sync](#sync)
15+
- [Exit codes](#exit-codes)
16+
- [Where to go next](#where-to-go-next)
17+
18+
## Install
19+
20+
```bash
21+
curl -fsSL https://siphon.dev/install.sh | sh # Linux / macOS
22+
brew install nixrajput/siphon/siphon # Homebrew
23+
scoop install siphon # Scoop (Windows)
24+
```
25+
26+
Confirm it's on your `PATH`:
27+
28+
```bash
29+
siphon --version
30+
```
31+
32+
## Register a profile
33+
34+
A profile is a named connection. Store the password as a **secret reference**
35+
(`env:`, `keychain://`, `awssm://`) so the config file never holds plaintext.
36+
37+
```bash
38+
export PROD_DB_PASS=''
39+
siphon profile add prod \
40+
--driver postgres \
41+
--host db.example.com \
42+
--user app_user \
43+
--password 'env:PROD_DB_PASS' \
44+
--database app_prod \
45+
--sslmode require
46+
47+
siphon profile list
48+
```
49+
50+
Inspect the schema to confirm the connection works:
51+
52+
```bash
53+
siphon inspect prod # tables, row estimates, on-disk sizes
54+
```
55+
56+
## Back up
57+
58+
```bash
59+
siphon backup prod # writes a checksummed dump to the catalog
60+
siphon dumps list # newest first; note the dump id
61+
```
62+
63+
Each dump is a single file prefixed with a metadata envelope and recorded with a
64+
SHA-256 checksum in a sidecar.
65+
66+
## Verify
67+
68+
```bash
69+
siphon verify <dump-id> # re-hashes the dump against its recorded checksum
70+
```
71+
72+
A mismatch exits with the integrity code (see below), so CI can catch corruption
73+
or tampering.
74+
75+
## Restore
76+
77+
```bash
78+
siphon restore --profile staging --dump <dump-id> --clean
79+
```
80+
81+
`--clean` drops and recreates objects before loading. For an incremental dump,
82+
`restore` resolves and replays the whole base→incremental chain in order.
83+
84+
## Sync
85+
86+
Back up the source and restore into the target in one streamed pass — no
87+
intermediate file on disk:
88+
89+
```bash
90+
siphon sync prod staging
91+
```
92+
93+
A backup failure propagates to the restore side, so a truncated dump is never
94+
committed as if it were clean.
95+
96+
## Exit codes
97+
98+
siphon uses a POSIX-friendly taxonomy so scripts and CI behave correctly:
99+
100+
| Code | Meaning |
101+
| --- | --- |
102+
| `0` | success |
103+
| `1` | user error (bad input, missing profile, failed confirmation) |
104+
| `2` | system error (I/O, network, the underlying tool failed) |
105+
| `3` | integrity failure (a checksum did not match) |
106+
| `130` | cancelled (Ctrl-C) |
107+
108+
So `siphon backup prod && upload-somewhere` only uploads on a clean backup.
109+
110+
## Where to go next
111+
112+
- [Configuration reference](CONFIGURATION.md) — the full config-file schema.
113+
- [Incremental backup](INCREMENTAL.md), [cross-engine sync](CROSS_ENGINE.md),
114+
and [CDC](CDC.md) — the advanced transfer modes.
115+
- [Storage backends](STORAGE.md) and [retention](RETENTION.md) — where dumps
116+
live and how they're pruned.
117+
- [Operational features](OPS.md) — audit log, 2FA, telemetry, schedule, tunnel.

web/app/globals.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,50 @@
9090
background-size: 2px 200px;
9191
animation: flow-down 6s linear infinite;
9292
}
93+
/* Orchestrated entrance: hero pieces settle in on load, staggered via
94+
--rise-delay. Scroll-revealed elements (.reveal) rise once when they enter
95+
the viewport, reinforcing the page's top-to-bottom "flow" metaphor. */
96+
@keyframes rise {
97+
from {
98+
opacity: 0;
99+
transform: translateY(14px);
100+
}
101+
to {
102+
opacity: 1;
103+
transform: none;
104+
}
105+
}
106+
.rise {
107+
animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
108+
animation-delay: var(--rise-delay, 0s);
109+
}
110+
/* Scroll reveal: hidden until JS adds .is-in (IntersectionObserver). */
111+
.reveal {
112+
opacity: 0;
113+
transform: translateY(16px);
114+
transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
115+
transition-delay: var(--rise-delay, 0s);
116+
}
117+
.reveal.is-in {
118+
opacity: 1;
119+
transform: none;
120+
}
121+
93122
@media (prefers-reduced-motion: reduce) {
94123
.flowline {
95124
animation: none;
96125
}
97126
html {
98127
scroll-behavior: auto;
99128
}
129+
/* No motion: everything is simply present, never hidden. */
130+
.rise,
131+
.reveal {
132+
animation: none;
133+
transition: none;
134+
opacity: 1;
135+
transform: none;
136+
}
100137
}
101138

102139
/* Docs prose: render the repo Markdown legibly on the dark base. */

0 commit comments

Comments
 (0)