Skip to content

Commit 83523eb

Browse files
uburuntucursoragent
andcommitted
Finish v4 cleanup and remove obsolete compatibility paths
Remove superseded pre-v4 state and rendering layers, preserve current v4 recovery semantics, and align generated contracts and guidance around one coherent architecture. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 5cea4f8 commit 83523eb

234 files changed

Lines changed: 2323 additions & 8655 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/pre-push

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,14 @@ check "CHANGELOG has VERSION entry" bash -c '
4242
grep -q "## \[$VERSION\]" CHANGELOG.md
4343
'
4444

45-
check "App links match (template ↔ apps.json)" python3 -c "
46-
import json, re, sys
47-
def extract_template(p):
48-
return sorted(set(re.findall(r'https://(?:play\.google\.com|apps\.apple\.com|github\.com/\S+/releases)[^\s\"<]*', open(p).read())))
49-
def extract_json(p):
50-
return sorted(set(item['url'] for item in json.load(open(p)) if re.match(r'https://(?:play\.google\.com|apps\.apple\.com|github\.com/)', item['url'])))
51-
t = extract_template('src/meridian/templates/connection-info.html.j2')
52-
d = extract_json('website/src/data/apps.json')
53-
if t != d:
54-
print(f'Template: {t}')
55-
print(f'Data: {d}')
56-
sys.exit(1)
57-
"
45+
check "PWA app metadata matches apps.json" python3 tests/validate_apps_sync.py
5846

5947
# --- Shell scripts ---
6048

6149
if command -v shellcheck &>/dev/null; then
6250
check "shellcheck install.sh" shellcheck -e SC2086,SC2029,SC2087,SC2155 -S warning install.sh
63-
check "shellcheck setup.sh" shellcheck -e SC2086,SC2029,SC2087,SC2155 -S warning setup.sh
6451
else
6552
check "bash -n install.sh" bash -n install.sh
66-
check "bash -n setup.sh" bash -n setup.sh
6753
fi
6854

6955
# --- Python quality (requires uv + dev deps) ---
@@ -72,7 +58,7 @@ if command -v uv &>/dev/null; then
7258
check "ruff check" uv run ruff check src/ tests/
7359
check "ruff format" uv run ruff format --check src/ tests/
7460
check "mypy" uv run mypy src/meridian/
75-
check "pytest" uv run pytest tests/ -q --tb=line --ignore=tests/test_integration_3xui.py
61+
check "pytest" uv run pytest tests/ -q --tb=line
7662
check "templates render" uv run python tests/render_templates.py
7763
else
7864
echo " ${DIM}(uv not found — skipping Python checks)${RESET}"

.github/CODEOWNERS

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
* @uburuntu
33

44
# High-risk files — extra scrutiny
5-
# Connection info templates (must stay in sync)
5+
# Connection page templates
66
src/meridian/templates/ @uburuntu
77

8-
# Credential handling (security-sensitive)
9-
src/meridian/credentials.py @uburuntu
8+
# Cluster state and secret handling (security-sensitive)
9+
src/meridian/cluster.py @uburuntu
10+
src/meridian/cluster_persistence.py @uburuntu
1011
src/meridian/ssh.py @uburuntu
1112

1213
# Provisioner (server deployment logic)

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ body:
2626
label: Setup mode
2727
options:
2828
- Standalone (no domain)
29-
- Domain mode (with Caddy + HAProxy)
29+
- Domain mode (nginx + Cloudflare CDN)
3030
- Uninstall
3131
- "Not sure"
3232
validations:

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@
1212
- [ ] `make ci` passes locally
1313
- [ ] Updated relevant documentation surfaces (see below)
1414

15-
### If modifying connection info templates:
16-
- [ ] All connection-info templates are in sync (CSS/JS/app links)
15+
### If modifying connection pages:
16+
- [ ] PWA templates, assets, and app metadata are in sync
1717
- [ ] Tested light and dark mode
1818

1919
### If adding a new CLI command:
2020
- [ ] Added help smoke test in `tests/test_cli.py`
2121
- [ ] Updated README.md commands table
2222
- [ ] Updated CLAUDE.md subcommands list
2323

24-
### If modifying credential handling:
25-
- [ ] Tested with existing credential files (backward compat)
26-
- [ ] Updated `ServerCredentials` dataclass if needed
24+
### If modifying cluster state or secrets:
25+
- [ ] Tested loading and saving an existing v4 `cluster.yml`
26+
- [ ] Updated the relevant typed cluster model
2727

2828
### If modifying provisioner steps:
2929
- [ ] Step returns proper StepResult (ok/changed/skipped/failed)

.github/workflows/CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
**Two-stage pipeline** — CI validates on every push/PR. Release triggers only on CI success on main via `workflow_run`. Prevents accidental releases from failed builds.
66

7-
**CI jobs**: Python Tests (3.10 + 3.12 matrix), Lint, Type Check, Validate (templates + app links + VERSION + CHANGELOG + deploy CLI flags + PWA demo), Shell (shellcheck), System Lab (multi-node Docker deploy), Website Build.
7+
**CI jobs**: Python Tests (3.11–3.13 matrix), Lint, Type Check, Validate (PWA rendering + app metadata + VERSION + CHANGELOG + deploy CLI flags), Shell (shellcheck), System Lab (multi-node Docker deploy), Website Build.
88

99
**Website package manager** — website CI uses pnpm with `website/pnpm-lock.yaml`; keep supply-chain guardrails in `website/pnpm-workspace.yaml`.
1010

@@ -14,7 +14,7 @@
1414

1515
## What's done well
1616

17-
- **Validate job** — single job checks templates render, app links match across surfaces, VERSION is valid semver, CHANGELOG has an entry, deploy CLI flags are documented in cli-reference.md. Catches drift between docs and code.
17+
- **Validate job** — single job checks the PWA renders, app metadata matches across surfaces, VERSION is valid semver, CHANGELOG has an entry, and deploy CLI flags are documented in cli-reference.md. Catches drift between docs and code.
1818
- **System lab depends on lint+test** — syntax must be clean before spinning up Docker. Saves CI minutes on obvious failures.
1919
- **PWA demo validation** — CI generates a demo PWA page and verifies all required files exist, SW is disabled for static hosting, and client HTML renders correctly.
2020
- **Contract drift checks** — Python validate runs `scripts/export_contracts.py --check`; website build runs `pnpm run contracts:check`.

.github/workflows/ci.yml

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,6 @@ jobs:
8686
pip install jinja2
8787
python3 tests/render_templates.py
8888
89-
- name: Verify connection-info app links match website data
90-
run: |
91-
python3 -c "
92-
import json, re, sys
93-
template = 'src/meridian/templates/connection-info.html.j2'
94-
data_file = 'website/src/data/apps.json'
95-
def extract_links(path):
96-
content = open(path).read()
97-
return sorted(set(re.findall(r'https://(?:play\.google\.com|apps\.apple\.com|github\.com/\S+/releases)[^\s\"<]*', content)))
98-
def extract_json_links(path):
99-
data = json.load(open(path))
100-
return sorted(set(item['url'] for item in data if re.match(r'https://(?:play\.google\.com|apps\.apple\.com|github\.com/)', item['url'])))
101-
baseline = extract_links(template)
102-
data_links = extract_json_links(data_file)
103-
if data_links != baseline:
104-
print(f'MISMATCH between template and website/src/data/apps.json')
105-
print(f' Template: {baseline}')
106-
print(f' Data: {data_links}')
107-
sys.exit(1)
108-
print(f'OK: App download links match between template and apps.json')
109-
"
110-
11189
- name: Verify VERSION file format
11290
run: |
11391
VERSION=$(cat VERSION | tr -d '[:space:]')
@@ -148,14 +126,10 @@ jobs:
148126
- uses: actions/checkout@v5
149127

150128
- name: Check script syntax
151-
run: |
152-
bash -n install.sh
153-
bash -n setup.sh
129+
run: bash -n install.sh
154130

155131
- name: Shellcheck
156-
run: |
157-
shellcheck -e SC2086,SC2029,SC2087,SC2155 -S warning install.sh
158-
shellcheck -e SC2086,SC2029,SC2087,SC2155 -S warning setup.sh
132+
run: shellcheck -e SC2086,SC2029,SC2087,SC2155 -S warning install.sh
159133

160134
system-lab:
161135
name: System Lab
@@ -210,7 +184,6 @@ jobs:
210184

211185
- name: Validate PWA demo generation
212186
run: |
213-
sudo apt-get install -y qrencode
214187
uv run meridian dev preview --output website/dist/demo
215188
# Verify expected files exist
216189
test -f website/dist/demo/index.html

.github/workflows/release.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,16 @@ jobs:
5252
uses: astral-sh/setup-uv@v7
5353

5454
- name: Generate PWA demo page
55-
run: |
56-
sudo apt-get install -y qrencode
57-
uv run meridian dev preview --output website/dist/demo
55+
run: uv run meridian dev preview --output website/dist/demo
5856

59-
- name: Add deploy artifacts
57+
- name: Add website artifacts
6058
run: |
61-
# Copy CLI files into Astro dist
59+
# Copy the CLI installer into Astro dist
6260
cp install.sh website/dist/install.sh
63-
cp setup.sh website/dist/setup.sh
6461
cp VERSION website/dist/version
6562
# Generate checksums
6663
cd website/dist
67-
sha256sum install.sh setup.sh > SHA256SUMS
64+
sha256sum install.sh > SHA256SUMS
6865
cd ../..
6966
# Bundle AI docs
7067
mkdir -p website/dist/ai

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ src/meridian/data/ai-reference.md
3030

3131
# GitHub Pages build artifacts (generated by deploy workflow)
3232
docs/install.sh
33-
docs/setup.sh
3433
docs/version
3534
docs/SHA256SUMS
3635
docs/ai/reference.md

AGENTS.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ src/meridian/CLAUDE.md — Python CLI architecture
2222
panel_bootstrap.py — panel setup, node deploy, xray config
2323
relay_ops.py — relay infrastructure operations
2424
ssh_keys.py — host key utilities (shared by ssh.py + engine)
25-
core/defaults.py — core-owned constants (DEFAULT_SNI)
26-
core/errors.py — shared error base types
25+
core/CLAUDE.md — transport-neutral API contracts
26+
defaults.py — core-owned constants (DEFAULT_SNI)
27+
errors.py — shared error base types
2728
commands/CLAUDE.md — per-subcommand pattern
2829
wizard.py — interactive deploy wizard
30+
diagnostics/CLAUDE.md — reusable server health checks
2931
engine/CLAUDE.md — local Engine use-case boundary
3032
routes.py — FastAPI route handlers
3133
provision/CLAUDE.md — step pipeline + idempotency
@@ -55,13 +57,13 @@ CLAUDE.md files. All of these are enforced in code review.
5557
- **Demo data**: use RFC 5737 IPs (`198.51.100.x`) in tests, examples, and docs. Never real IPs.
5658
- **Privacy**: no real names, server IPs, or domains in commits, code, or public docs unless the user explicitly asks.
5759
- **Self-hosted**: zero external HTTP requests at runtime from connection pages, CLI update checks aside. Target users are in regions that block CDNs.
58-
- **Commit per change**: one logical change per commit. Footer `Refs: uburuntu/meridian#NN` when resolving an issue.
60+
- **Commit per change**: one logical change per commit. Footer `Refs: getmeridian/meridian#NN` when resolving an issue.
5961
- **Ask before posting** anything public — GitHub issues, PR descriptions, comments, discussions. Always show the text first.
6062

6163
## Project management
6264

6365
- High-level direction: [ROADMAP.md](ROADMAP.md)
64-
- Concrete trackable work: [GitHub issues](https://github.com/uburuntu/meridian/issues)
66+
- Concrete trackable work: [GitHub issues](https://github.com/getmeridian/meridian/issues)
6567
- Shipped history: [CHANGELOG.md](CHANGELOG.md)
6668
- Security policy: [SECURITY.md](SECURITY.md)
6769
- Contribution guide: [CONTRIBUTING.md](CONTRIBUTING.md)

CHANGELOG.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,7 @@ All notable changes to Meridian are documented here.
44

55
Format follows [Keep a Changelog](https://keepachangelog.com/).
66

7-
## [Unreleased]
8-
9-
### Added
10-
- **meridian-core JSON contract foundation** — Pydantic-backed `meridian.output/v1` envelopes, structured summary/error/event models, JSONL event primitives, JSON Schema export, and centralized secret redaction for automation and future UI clients
11-
- **`meridian api schemas` / `meridian api schema NAME`** — discover and export JSON Schemas for the public meridian-core contracts used by CLI JSON output and future UI clients
12-
13-
### Changed
14-
- **`meridian plan --json`** now emits the shared envelope shape with plan details under `data`; `status` is `no_changes` or `changed`, and the process exit code remains `0` for converged and `2` for changes pending
15-
- **`meridian fleet status --json`** and **`meridian fleet inventory --json`** now emit the shared envelope shape while preserving stable command fields under `data`
16-
17-
## [4.0.0] - 2026-04-12
7+
## [4.0.0] - Unreleased
188

199
### Remnawave compatibility matrix
2010
This release pins a single tested version tuple. Upgrading any one component
@@ -24,43 +14,51 @@ without the rest is not supported — move the whole set together.
2414
|---|---|
2515
| `remnawave/backend` | `2.7.4` |
2616
| `remnawave/node` | `2.7.0` |
27-
| `remnawave/subscription-page` | `7.1.8` |
17+
| `remnawave/subscription-page` | `7.2.1` |
2818
| `remnawave` Python SDK | `2.7.1` |
2919
| Xray-core client (test binary) | `26.3.27` |
3020
| Realm (relay forwarder) | `2.9.3` |
3121
| Pebble (system-lab ACME CA) | `2.10.0` |
3222

3323
### Changed
24+
- **`meridian plan --json`** now emits the shared envelope shape with plan details under `data`; `status` is `no_changes` or `changed`, and the process exit code remains `0` for converged and `2` for changes pending
25+
- **`meridian fleet status --json`** and **`meridian fleet inventory --json`** now emit the shared envelope shape while preserving stable command fields under `data`
3426
- **Remnawave replaces 3x-ui** — modern panel with panel/node separation (NestJS + PostgreSQL + Valkey), proper REST API, built-in subscriptions, native multi-node support
3527
- **Single `cluster.yml` replaces per-server `proxy.yml`** — fleet-wide manifest with panel URL, API token, nodes, relays. Client state lives in Remnawave's database, not locally
3628
- **`client add` is one API call** — was ~400 LOC of SSH-tunneled curl, credential sync, and per-inbound client insertion
3729
- **Relay = Remnawave Host entry** — enable/disable host toggles subscription inclusion automatically
3830

3931
### Added
32+
- **meridian-core JSON contract foundation** — Pydantic-backed `meridian.output/v1` envelopes, structured summary/error/event models, JSONL event primitives, JSON Schema export, and centralized secret redaction for automation and future UI clients
33+
- **`meridian api schemas` / `meridian api schema NAME`** — discover and export JSON Schemas for the public meridian-core contracts used by CLI JSON output and future UI clients
4034
- **`meridian node add/list/remove/check`** — multi-node fleet management
4135
- **`meridian fleet status/recover`** — panel health, node connectivity, relay status, user count, and reconstruct-from-panel when local state is lost
42-
- **`meridian migrate`** — guided migration from 3.x (reads old proxy.yml, prints step-by-step plan)
4336
- **`MeridianPanel` REST client** — wraps the official `remnawave` Python SDK (v2.7.1) with retries, credential redaction, and thread-local event loops for parallel workers
4437
- **Config reliability** — corrupt YAML handling, version check, backup before mutations, disk-full error messages, external-edit guard (`cluster.save()` refuses to clobber if the file mtime advanced during a long-running apply), snapshot type validation
4538
- **Reality keys persisted** — public_key and short_id saved in cluster.yml for connection testing
4639
- **Declarative plan/apply workflow**`cluster.yml` becomes desired state (`desired_nodes`, `desired_relays`, `desired_clients`, `subscription_page`); `meridian plan` prints a Terraform-style diff; `meridian apply` converges. Imperative commands (`deploy`, `node add`, `client add`) mirror their effect into `desired_*` when the list is non-null — hybrid sync, mixing the two modes is safe
47-
- **Applied-state tracking** — every successful `apply` snapshots desired state into `cluster._extra["desired_*_applied"]`. The next plan distinguishes intentional removals (in applied → executes under `--yes`) from drift (not in applied → requires `--prune-extras=yes`). Closes the subtle bug where `--yes` silently skipped deliberate removals
40+
- **Applied-state tracking** — every successful `apply` snapshots desired state into the typed `cluster.applied_state` model. The next plan distinguishes intentional removals (in applied → executes under `--yes`) from drift (not in applied → requires `--prune-extras=yes`). Closes the subtle bug where `--yes` silently skipped deliberate removals
4841
- **`meridian plan --json`** — structured output for CI consumption; exit 0 = converged, 2 = changes pending, 1 = error. Stable JSON shape with typed `actions[].kind` values
4942
- **`--prune-extras=ask|yes|no`** — explicit control over drift handling. Under `--yes`, `ask` downgrades to `no` (safety default); destructive actions still require one confirmation unless `--yes`
50-
- **Parallel node provisioning**`ThreadPoolExecutor` with `--parallel N` (default 4); per-worker `MeridianPanel` SDK instance, `threading.local()` event loops, `threading.RLock` on `cluster.save()` for safe concurrent writes
43+
- **Parallel node provisioning**`ThreadPoolExecutor` with `--parallel N` (default 4); per-worker `MeridianPanel` SDK instance, `threading.local()` event loops, and serialized cluster saves
5144
- **SSH multiplexing (`ControlMaster`)** — connection reuse across all SSH operations
5245
- **Warp tri-state**`DesiredNode.warp: None | False | True` (keep-current / disable / enable) with correct YAML round-trip (explicit `null`, not dropped; loader defaults missing key to `None`)
5346
- **YAML null semantics** for `desired_*` and `subscription_page``null` means "unmanaged" (as documented); previously `desired_clients: null` collapsed to `[]` with `manage=True` and `subscription_page: null` loaded as `enabled=True`
5447
- **Duplicate node-name validator**`compute_plan`'s name→IP map and `find_node()` disagreed on duplicates, which could misroute relay `exit_node` references. Caught at load time now
5548
- **Real-VM test harness** — optional `tests/realvm/` provisions real cloud VMs (Hetzner via `hcloud-python` SDK), runs full deploy + tier-α verification, tears down. Local-only (never in CI), opt-in via `make real-lab`. Foundation for a future `meridian deploy --create-vm <provider>` feature. Per-cloud `CloudProvider` abstract class under `src/meridian/infra/providers/`
5649
- **NET_ADMIN capability** on remnawave-node container — required by panel 2.6.2+ for Torrent Blocker, IP Control, and related plugins. Without it, those features silently no-op
5750
- **Subscription-page lifecycle via cluster.yml** — enable/disable the Remnawave subscription container declaratively; `docker compose up -d --no-recreate` on subpage-only apply to avoid incidental panel restart
51+
- **Hysteria2 UDP/443 fallback** — available after the TCP transports in subscription ordering, with matching host and firewall configuration
5852
- **`ConfigureFail2ban` + fail2ban package wired in both pipelines** — fail2ban is now actually installed and started on every hardened deploy. Previously `build_setup_steps` had an operator-precedence trap (`InstallPackages(REQUIRED_PACKAGES + ["fail2ban"] if harden else None)` parsed as `(REQUIRED + fail2ban) if harden else None`, skipping all packages on `--no-harden`) and `build_node_steps` (used for redeploy) lacked the step entirely
5953
- **Smart xray readiness polling** in system-lab — polls node API port 3010 + 5s grace for Reality inbound init, replacing a blind `sleep 30`
6054

55+
### Fixed
56+
- **Safe fleet recovery** — recovered inbounds now use stable protocol keys and the current cluster schema; Reality public keys are derived from preserved private keys, and redeploy refuses incomplete key material instead of silently rotating credentials
57+
6158
### Removed
6259
- **3x-ui panel** — PanelClient, ConfigurePanel, CreateInbound, all SSH-tunneled curl API calls
63-
- **Per-server proxy.yml** — replaced by cluster.yml (kept only for `meridian migrate` compatibility)
60+
- **All pre-v4 migration and compatibility paths** — v4 starts from typed `cluster.yml` and `servers.json` state; the old migrate command, installer/setup shims, per-server `proxy.yml` credentials, legacy registry import, and 3x-ui cleanup step are gone
61+
- **Legacy local connection-page renderer** — the server-hosted PWA is the only connection-page implementation
6462
- **Local client state** — no more UUID storage, credential sync, SCP rollback
6563
- **Legacy HAProxy + Caddy code paths** — replaced entirely by nginx (stream SNI routing + http TLS + reverse proxy)
6664

0 commit comments

Comments
 (0)