refactor: move clients under REMAPPING deployable grammar

Land desktop, web, mobile, and admin-web at REMAPPING/meridian-* with
rewritten build/CI paths, the IHLC scaffold, and an updated STELLAR snapshot
so the CTO-required one-directory-per-deployable layout is the live tree.

Signed-off-by: Joshua Belke <joshua@innovationhub-act.org>
This commit is contained in:
Josh Belke 2026-08-10 21:38:15 -04:00
commit c6dcd136b1
2987 changed files with 6596 additions and 1119 deletions

View file

@ -224,3 +224,29 @@
{"id":"int-e2160a39","kind":"field_change","created_at":"2026-08-09T01:51:23.949245Z","actor":"Joshua Belke","issue_id":"meridian-4z3.14","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Both acceptance criteria met with mutation-proven evidence, across three build rounds with two independent fresh-context red teams between them.\n\n(1) A test induces a real broadcast Lagged burst and proves the consumer RESUMES: consumer_resumes_delivery_after_a_real_lag_burst in crates/meridian-pubsub/src/broadcast_consumer.rs, using a real tokio broadcast channel (cap 2, 6 pre-sends), time-bounded so terminal behaviour fails rather than hangs. Mutation - make the Lagged arm terminal -> exit 101; restored -> exit 0.\n\n(2) The absorbing fault is removed for lag and the loss is observable. mark_application_fault is now reserved for the two genuinely unrecoverable outcomes (closed channel, panicked consumer); lag never moves phase at all. The loss is recorded as fact: the three per-lane *_lag_total counters, last_lag_unix, and a monotonic meridian_pubsub_subscriber_skipped_messages counter, all surfaced in the /ready diagnostic and paged by a new MeridianPubsubConsumerSkippedMessages alert with a real runbook section.\n\nFAILS IF recovery needs a pod restart: it does not - the consumer resumes in place.\nMUST NOT BREAK ordering/dedup: cargo test -p meridian-pubsub --lib exit 0, 32 passed; the protected pin 'application faults are absorbing until process restart' is untouched and green.\n\nWHAT THE RED TEAMS CHANGED, recorded because the naive fix was WRONG: resuming without reconciling removed an authorization backstop (meridian-74iz) - the old fail-closed path guaranteed a socket drain that dropped banned members' sockets, since an established WebSocket never re-auths. The fix is a per-lane reconcile hook run synchronously before delivery resumes: ConnectionControl flushes authz caches and restarts all local sockets, CacheInvalidation flushes, Events no-ops. Also closed on the way: a second phase writer publishing false Ready (meridian-gmx8), three metrics silently dropped from the observability guard (meridian-ewwt), and the lane-to-compensation wiring being bound by no test at all (meridian-elo4).\n\nVERIFIED BY THE CLOSER, not quoted: cargo test -p meridian-relay --lib --bins exit 0 (865 passed / 51 ignored); cargo test -p meridian-pubsub --lib exit 0 (32 passed); just check-alert-runbooks exit 0 ('Validated 26 alerts across 3 charts against 160 declared metrics'); clippy -D warnings and cargo fmt --check exit 0.\n\nNOT CLOSED, deliberately: meridian-ffmv - the drain now fires on every burst with no damping, and adding a cooldown weakens the guarantee just restored, so the trade is a human call. The DoS shape behind it is named but UNMEASURED. Per Design Law this is also all static analysis plus unit tests; no live relay re-probe of the drain-and-re-auth sequence has been run."}}
{"id":"int-5d71fcfe","kind":"field_change","created_at":"2026-08-09T01:53:57.519248Z","actor":"Joshua Belke","issue_id":"meridian-thst","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Closed as recorded-and-repaired, not as a re-opened decision. The defect itself is fixed: G17's evidence cell now reads 26 alerts / 160 declared metrics, both integers re-derived from 'just check-alert-runbooks' rather than quoted.\n\nThe finding's larger point is recorded in the register at F105 and stops there. Between its filing and now the owner ACCEPTED the prose/number contradiction class as residual risk on meridian-nwo2 and refused a fourth guard round, and that ruling is broader than the size hypothesis this evidence falsifies - so this does not re-open anything, and the loop may not accept or re-open residual risk on its own authority either way.\n\nWhat is worth keeping for a future revisit: the mechanism is NOT cell size. A 180-character cell drifted exactly as an 11,000-character one did. It drifts because a COUNT is copied into a cell once and never re-derived, and goes stale from unrelated work elsewhere in the tree - nobody adding a metric in crates/ has any reason to open the register. Note the ALERT count did not drift; only the number that moves as a side effect of someone else's commit. And all three known instances are numeric, so the regex that was built and rejected would have caught none of them."}}
{"id":"int-586e9387","kind":"field_change","created_at":"2026-08-09T02:01:00.473908Z","actor":"Joshua Belke","issue_id":"meridian-46z4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
{"id":"int-8ffa401e","kind":"field_change","created_at":"2026-08-09T15:47:39.077033Z","actor":"Joshua Belke","issue_id":"meridian-ej9e.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Thread download shipped: composer toolbar format menu (JSON/Markdown/LaTeX/DOCX); DOCX is hand-written WordprocessingML over shared stored-only zip with hyperlink relationships; PDF deferred per Experience council. Tests green; desktop-check exit 0."}}
{"id":"int-c4fdf509","kind":"field_change","created_at":"2026-08-09T15:47:57.355354Z","actor":"Joshua Belke","issue_id":"meridian-d02c","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Human-authorised G15 bar reset. Clause (a) is now class-conditional: measured figures require a named just recipe; estimate/target/vendor-published figures require an explicit marker and basis (a'). Profile (b) and interval (c) unchanged. Register Bars + F072 updated; G15 evidence cell notes the unblock."}}
{"id":"int-11a4b11c","kind":"field_change","created_at":"2026-08-09T15:47:57.850534Z","actor":"Joshua Belke","issue_id":"meridian-wtwe","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Scoped absent capabilities out of G21 title the G03/G14 way. Title is now Operator console — single-relay metrics and registry health. Scope table records credentials (atqz), retained state (1ugp.3/.7), per-pod node health (1ugp.6), multi-node fleet monitoring (1ugp). Bar + baseline + F086 updated."}}
{"id":"int-d728654f","kind":"field_change","created_at":"2026-08-09T15:47:58.705739Z","actor":"Joshua Belke","issue_id":"meridian-cwgu","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"False diagnosis — original Justfile sourcing of .env.local over dotenv-applied .env is the documented layering. Wrong fix was reverted (61731cf3d). Residual MERIDIAN_TEST_* override docs filed as meridian-vvoe."}}
{"id":"int-21da0536","kind":"field_change","created_at":"2026-08-09T15:47:59.329444Z","actor":"Joshua Belke","issue_id":"meridian-klpo","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Already fixed by meridian-4z3.21 / 81d1f318e. Test asserts dispatcher.register_callsite(...).is_never() instead of tracing::enabled!, bypassing the process-global callsite interest cache. Duplicate of the closed sibling."}}
{"id":"int-b643e6c2","kind":"field_change","created_at":"2026-08-09T15:47:59.81748Z","actor":"Joshua Belke","issue_id":"meridian-0amv","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Retry loop does not reproduce on a fresh build (comment 2026-08-07). Landed the operator-visible half: normalizeRelayFailure keeps bare string / message-envelope relay reasons, and mention/membership subscribe failures log the reason string. Relay-side denied-REQ NOTICE observability left as follow-up if it reappears."}}
{"id":"int-23ae0acc","kind":"field_change","created_at":"2026-08-09T15:52:31.349481Z","actor":"Joshua Belke","issue_id":"meridian-c2ws","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Acceptance: shared-worktree push verification rule encoded.\n\nCommit: 1b4de1e52e64a6a876f450877c39b9a5752bb16d\nFiles:\n- skills/run-meridian-gauntlet/SKILL.md (replaces git rev-list --count @{u}..HEAD # must print 0 with git fetch + git branch -r --contains <sha>; adds Shared-worktree push verification paragraph)\n- .settings/stellar/gauntlet-loop.md (integrator closeout: verify with branch -r --contains, never @{u}..HEAD count; do not push neighbour ahead commits)\n\nRule (skill): To verify *my* commit landed, use git branch -r --contains <sha> after git fetch — never git rev-list --count @{u}..HEAD / @{u}..HEAD == 0. The latter answers \"is the branch fully pushed,\" a different question; neighbour unpushed commits inflate the count. Do not push commits that show as ahead when they are not yours.\n\nVerify: rg finds Shared-worktree push verification + branch -r --contains in both files; old must-print-0 post-push line absent."}}
{"id":"int-c1d0aa97","kind":"field_change","created_at":"2026-08-09T15:53:42.69604Z","actor":"Joshua Belke","issue_id":"meridian-vvoe","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Documented MERIDIAN_TEST_DATABASE_URL / MERIDIAN_TEST_REDIS_URL / MERIDIAN_TEST_RELAY_URL next to recipes + CONTRIBUTING; env-doctor notes layer provenance. Evidence: 4bf53a8f5; rg shows all three names in Justfile and CONTRIBUTING.md."}}
{"id":"int-b7b9e0a8","kind":"field_change","created_at":"2026-08-09T16:17:50.993007Z","actor":"Joshua Belke","issue_id":"meridian-37cx.1","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
{"id":"int-2115d619","kind":"field_change","created_at":"2026-08-09T16:17:58.970017Z","actor":"Joshua Belke","issue_id":"meridian-37cx.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Item 1 done: live curl Accept:text/html returns HTML page not JSON; regression test committed as 3760feddd"}}
{"id":"int-d6539c82","kind":"field_change","created_at":"2026-08-09T16:22:40.323259Z","actor":"Joshua Belke","issue_id":"meridian-qd12","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"VIEW HALF ONLY — closed with evidence. Install-on-demand remains blocked per Architecture & Scale council (placement + sandbox + IMPLEMENTABLE profile still unmet).\n\nEvidence:\n- Commit 5fe766d088f9b0d825baff2a299011a5fb3e80d7 (worktree detached HEAD; not pushed)\n- Files: desktop/src/shared/mips/capability.ts (+profileIngestMips, packSpeakability), capability.test.mjs, index.ts; desktop/src/features/settings/ui/CapabilitiesSettingsCard.tsx (Feed profiles section = register filter, substrate split so no duplicate); CapabilityDetailSheet.tsx (speakability row); capabilityCopy.ts (FEED_PROFILE_SECTION — no Install label); desktop/AGENTS.md contract note\n- Tests: node --test desktop/src/shared/mips/capability.test.mjs → 31 pass / 0 fail (profileIngestMips + packSpeakability suites)\n- Acceptance: Capabilities answers \"can this client speak AIS yet, and what is blocking it\" from existing register + NIP-11 via packSpeakability; Profile/Ingest framed as filter over one register, never a second decoder list\n- Explicitly NOT done: install-on-demand, second registry, feeder decoding, codecs"}}
{"id":"int-14d4c6d1","kind":"field_change","created_at":"2026-08-09T16:34:54.216621Z","actor":"Joshua Belke","issue_id":"meridian-3f4n","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"No-response wait capped at 5m (login-code TTL) with loopback-named errors; window close fails immediately. LOGIN_TIMEOUT remains 10m outer bound. Evidence: b2239e46d + no_response_path unit test."}}
{"id":"int-3782ffae","kind":"field_change","created_at":"2026-08-09T17:19:15.216747Z","actor":"josh","issue_id":"meridian-1ugp.2","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Delivered meridian_fanout_delivered_total on send_fanout_frames write-to-socket success path; distinguishable from meridian_fanout_recipients (intended).\n\nEvidence:\n- commit bb3f7883c622444784cb7708820a16fd10eda509 (detached worktree meridian-1ugp.2)\n- file: crates/meridian-relay/src/handlers/event.rs\n- cargo test -p meridian-relay --lib handlers::event::tests::pubsub_fanout::delivered_counter_lags_intended_when_subscriber_disconnects_mid_fanout\n → ok; asserts intended (2.0) > delivered (1) when subscriber rx dropped mid-fan-out"}}
{"id":"int-db91cd89","kind":"field_change","created_at":"2026-08-09T17:21:19.486476Z","actor":"Joshua Belke","issue_id":"meridian-1ugp.1","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Acceptance met on 589af86af29d8fc56a28bd0e7e3af0184870bf7a (detached worktree meridian-1ugp-1-ws-bytes).\n\nCounters: meridian_ws_bytes_received_total / meridian_ws_bytes_sent_total in connection.rs, labelled by community host; ingress before parse, egress after serialization in send_loop.\n\nEvidence: cargo test -p meridian-relay --lib connection::tests::known_size_publish_moves_ws_byte_counters → ok (known-size EVENT frame + OK response assert exact counter deltas). Full connection::tests:: → 9 passed. just check-kinds → exit 0. No wire/kind changes."}}
{"id":"int-1f21f002","kind":"field_change","created_at":"2026-08-09T17:28:03.548822Z","actor":"Joshua Belke","issue_id":"meridian-aonl","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Channel visibility cache already on ingest path (d1b93ccaa); miss/hit counters + unit evidence in 52bd99497"}}
{"id":"int-b15a7977","kind":"field_change","created_at":"2026-08-10T01:33:45.328796Z","actor":"Joshua Belke","issue_id":"meridian-dok","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"VERIFY_ONLY: _ensure-migrations already layers .env.local (Justfile:492-505 mirrors relay). cargo run -p meridian-admin -- migrate uses relocated DATABASE_URL. Evidence: recipe body sources .env.local before migrate; description's 'Fixed by' matches current tree. No code change."}}
{"id":"int-c1eb2c78","kind":"field_change","created_at":"2026-08-10T01:40:22.203457Z","actor":"Joshua Belke","issue_id":"meridian-dok","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"VERIFY_ONLY: _ensure-migrations already layers .env.local (Justfile:492-505 mirrors relay). Recipe sources .env.local before cargo run -p meridian-admin -- migrate. Description's Fixed-by matches current tree. No code change."}}
{"id":"int-8e649de3","kind":"field_change","created_at":"2026-08-10T01:42:34.0894Z","actor":"Joshua Belke","issue_id":"meridian-dgc","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"compose-check asserts DATABASE_URL host port matches MERIDIAN_PG_HOST_PORT and the override's published postgres port (plus MERIDIAN_CONTROL_DATABASE_URL / MERIDIAN_S3_ENDPOINT pairs). commit 7fac5dfa0d0274a529bb40f91b76fbcb83d787f4; just compose-check exit 0; deliberate DATABASE_URL:5432 vs published:5433 fails exit 1; unset MERIDIAN_PG_HOST_PORT still fails exit 1 on URL vs published."}}
{"id":"int-3b421178","kind":"field_change","created_at":"2026-08-10T01:46:40.431843Z","actor":"Joshua Belke","issue_id":"meridian-lwmo","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"npm supply-chain gate landed (SHA 7374d03aa).\n\nEvidence:\n- just check-npm-supply-chain → exit 0 (4 audit findings ignored with reason; 19 licence groups allowed)\n- node --test scripts/check-npm-supply-chain-core.test.mjs → 13/13 pass (includes synthetic unignored-advisory failure case)\n- Justfile check: deps include check-npm-supply-chain\n- Paths: scripts/check-npm-supply-chain{,-core.mjs,-core.test.mjs}.mjs, scripts/npm-supply-chain.json, Justfile, scripts/AGENTS.md\n- No push (builder brief)"}}
{"id":"int-913cac02","kind":"field_change","created_at":"2026-08-10T01:50:14.28737Z","actor":"Joshua Belke","issue_id":"meridian-37cx.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Disclosed browser-cookie vs desktop-loopback session disjointness on control-plane HTML.\n\nSHA: 8dc8b5a3e8e7368cb1a310ad17bd491facbaafec\nPaths: crates/meridian-control-plane/src/http.rs, crates/meridian-control-plane/tests/integration_control_plane.rs, crates/meridian-control-plane/AGENTS.md\n\nEvidence:\n- Relays sign-in card: \"Sign in to this browser\" / cookie session / does not sign into desktop app\n- Dev login !returnTo: \"Browser session only.\" + \"Continue in this browser\"\n- Dev login loopback returnTo: \"Desktop app session.\" + does not create browser session\n- Unit: cargo test -p meridian-control-plane --lib http::tests → 42 passed (exit 0); login_page_names_the_client_it_returns_to + relays_page_discloses_browser_only_sign_in ok\n- Integration written (browser_login_page_discloses_cookie_session); harness SKIP without reachable superuser Postgres (exit 0 skip)\n\nNO push (leaf brief). Parent meridian-37cx left open (LOGIN_TIMEOUT / remaining items)."}}
{"id":"int-1d2d9a13","kind":"field_change","created_at":"2026-08-10T01:56:54.206982Z","actor":"Joshua Belke","issue_id":"meridian-81a","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed ensure_future_partitions to split *_p_future on catch-all overlap (DETACH → CREATE TABLE … PARTITION OF per month → move non-generated columns → ATTACH remainder). Sibling overlap stays info; catch-all cover warns and carves. No migration rewrite of checksum-frozen 0001 — runtime manager is the carver.\n\nSHA: 55f5fec93b20865107e6f5d7960b84cf6c11c330\nPaths: crates/meridian-db/src/partition.rs, crates/meridian-db/src/test_scratch.rs, crates/meridian-db/AGENTS.md, migrations/AGENTS.md\n\nEvidence:\n- cargo test -p meridian-db partition → exit 0 (7 passed, 1 ignored)\n- cargo test -p meridian-db partition::postgres_tests -- --ignored → exit 0 (1 passed): current-month partition bounded exactly; catch-all advanced past months_ahead\n- git commit -s → exit 0; NO push (per brief)"}}
{"id":"int-0935d5d6","kind":"field_change","created_at":"2026-08-10T01:58:40.585856Z","actor":"Joshua Belke","issue_id":"meridian-66v","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"ACCEPTED. App-data migration excludes .window-state.json (display-local) via migration/app_data.rs; identity/agents still copy. Second launch cannot re-import off-screen geometry after delete because the skip list applies on every pass (test: second_pass_does_not_reintroduce_deleted_window_state). Evidence: cargo test --manifest-path desktop/src-tauri/Cargo.toml app_data → 6 passed exit 0; copy_dir_all → 1 passed exit 0. SHA 17089be1445e5c79c3dfb043af5387f5a032c205. No push (leaf brief)."}}
{"id":"int-d3f6b1af","kind":"field_change","created_at":"2026-08-10T01:59:08.541964Z","actor":"josh","issue_id":"meridian-4guo","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"ACCEPTED — machine onboarding gains post-identity CapabilityPacksStep: confirm baseline NIPs (1/11/29/42), then optionally elect AIS / ADS-B / APP-6E / STANAG-4609 via setPackElection (same store as Settings/Apps; packs ≠ MIPs).\n\nSHA: af3e0fcbe9343ea7c6a641b5d24b681ec3a51eae (worktree meridian-h73s, detached; no push)\n\nPaths:\n- desktop/src/features/onboarding/ui/CapabilityPacksStep.tsx\n- desktop/src/features/onboarding/ui/machineOnboardingPages.ts\n- desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx\n- desktop/src/shared/constants/onboardingPacks.ts\n- desktop/AGENTS.md (election contract note)\n\nVerify:\n- cd desktop && node --import ./test-loader.mjs --experimental-strip-types --test src/features/onboarding/ui/CapabilityPacksStep.test.mjs src/shared/constants/onboardingPacks.test.mjs → exit 0 (7 pass)\n- biome check --write on touched TS → exit 0\n\nUnlocks meridian-cbps: packs page exists before harness (backup→capabilities→setup). cbps may only need TOTAL_ONBOARDING_PAGES / community chrome audit.\n\nRemainder (not this slice): community-flow pack offer, Playwright E2E for the step, MAVLINK/TAK/REMOTE-ID first-run offer."}}
{"id":"int-d13db4d9","kind":"field_change","created_at":"2026-08-10T01:59:39.921409Z","actor":"Joshua Belke","issue_id":"meridian-kir","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Re-pointed both GitHub API-shaped URLs to git.office.ilab.zone (commit 8f109093a).\n\nProbe evidence (2026-08-10):\n1. GET https://git.office.ilab.zone/api/v1/repos/RAID/R2D2-MERIDIAN/releases?limit=10 → HTTP 200, body `[]`, X-Total-Count: 0. Forgejo OpenAPI (swagger.v1.json) confirms Release.assets[].browser_download_url + draft/prerelease and list query `limit=` (not per_page). Empty list → selectMeridianDownloadUrl returns undefined → fallback to MERIDIAN_RELEASES_URL page (allowed by bead until meridian-uec publishes artifacts).\n2. GET https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/raw/branch/main/README.md → HTTP 200, text/plain (confirmed Gitea/Forgejo raw form). Same form for crates/meridian-agent/README.md → 200. Avatar path meridian-agent.png → 404 (file absent in tree; URL form correct).\n\nPaths: web/src/shared/lib/meridian-download.ts, web/tests/e2e/smoke.spec.ts, desktop/src-tauri/src/managed_agents/discovery.rs. Removed DANGLING comments.\n\nVerify: pnpm typecheck (web) exit 0; cargo test --lib resolves_ (includes resolves_meridian_agent_avatar) 22 passed; node selectMeridianDownloadUrl empty→undefined + forgejo-shape→asset URL exit 0. No push (leaf builder brief)."}}
{"id":"int-4a60db03","kind":"field_change","created_at":"2026-08-10T02:00:59.074533Z","actor":"Joshua Belke","issue_id":"meridian-lyw4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Snapshot definition JSON + hash onto workflow_runs at trigger; approval resume rejects live hash drift with explicit Failed error_message and freezes to snapshot on match.\n\nSHA: a04306645af46632f4b9b9c093588c286ded4cf4\n\nPaths:\n- migrations/0032_workflow_run_definition_snapshot.sql\n- migrations/AGENTS.md\n- crates/meridian-db/src/workflow.rs\n- crates/meridian-db/src/lib.rs\n- crates/meridian-workflow/src/executor.rs (definition_for_approval_resume)\n- crates/meridian-workflow/src/lib.rs\n- crates/meridian-relay/src/handlers/command_executor.rs\n- crates/meridian-relay/src/api/bridge.rs (create_workflow_run signature)\n\nVerify:\n- cargo test -p meridian-workflow --lib approval_resume → EXIT 0 (3 passed)\n- cargo test -p meridian-db --lib workflow_run_record → EXIT 0 (6 passed)\n- cargo test -p meridian-relay --lib --no-run → EXIT 0\n- MERIDIAN_TEST_DATABASE_URL unset; ignored create_workflow_run_persists_definition_snapshot not run\n\nNo push (leaf builder brief)."}}
{"id":"int-1c2d6f3b","kind":"field_change","created_at":"2026-08-11T01:09:37.956806Z","actor":"Joshua Belke","issue_id":"meridian-htav","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}

File diff suppressed because one or more lines are too long

View file

@ -1,26 +1,26 @@
# Build outputs
target/
**/target/
desktop/src-tauri/target/
REMAPPING/meridian-desktop/src-tauri/target/
# Native non-relay UI builds (not part of the public image).
#
# Exception: web/ and admin-web/ map `@brand/*` onto `desktop/src/shared/ui/*`
# (see web/vite.config.ts + web/tsconfig.json), so the shared globe module is a
# Exception: REMAPPING/meridian-web/ and REMAPPING/meridian-admin-web/ map `@brand/*` onto `REMAPPING/meridian-desktop/src/shared/ui/*`
# (see REMAPPING/meridian-web/vite.config.ts + REMAPPING/meridian-web/tsconfig.json), so the shared globe module is a
# build input for the relay image even though the desktop app is not. Excluding
# all of desktop/ makes `pnpm -C web build` fail with TS2307 on
# all of REMAPPING/meridian-desktop/ makes `pnpm -C REMAPPING/meridian-web build` fail with TS2307 on
# `@brand/meridian-globe/mountGlobe`. The module only imports itself and react,
# so re-including this one directory is the whole dependency closure.
# The `**` matters: a bare directory exception re-includes only the directory
# entry, not the files under it, so the copy still comes up empty.
desktop/
!desktop/src/shared/ui/meridian-globe/**
mobile/
REMAPPING/meridian-desktop/
!REMAPPING/meridian-desktop/src/shared/ui/meridian-globe/**
REMAPPING/meridian-mobile/
# Node artifacts (built inside the image; host outputs would invalidate cache)
node_modules/
**/node_modules/
web/dist/
REMAPPING/meridian-web/dist/
# VCS, IDE, scratch
.git/

2
.gitattributes vendored
View file

@ -2,7 +2,7 @@
# file lands in the working copy with CRLF. Biome formats with LF
# (biome.json sets no lineEnding override), which fails `biome check` on
# effectively every file, and
# desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs asserts on
# REMAPPING/meridian-desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs asserts on
# patches/*.patch with `\n`-joined patterns. Normalize to LF in the working
# copy on every platform; the stored blobs are already LF.
* text=auto eol=lf

2
.github/AGENTS.md vendored
View file

@ -77,7 +77,7 @@ Also: `CODEOWNERS`, `PULL_REQUEST_TEMPLATE.md`, `ISSUE_TEMPLATE/`
`just check`/`just test-unit` map to Rust Lint/Unit Tests.
- Prefer sharding an existing E2E job over adding a new workflow; the desktop E2E
jobs already have a shard matrix.
- `scripts/summarize-flaky-tests.mjs` (in `desktop/scripts/`) exists for triaging
- `scripts/summarize-flaky-tests.mjs` (in `REMAPPING/meridian-desktop/scripts/`) exists for triaging
flaky E2E output — use it instead of retry-looping a job.
## Verification

View file

@ -57,19 +57,19 @@ jobs:
desktop:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'desktop/**'
- '!desktop/src-tauri/**'
- 'REMAPPING/meridian-desktop/**'
- '!REMAPPING/meridian-desktop/src-tauri/**'
- 'pnpm-lock.yaml'
desktop-rust:
- 'desktop/src-tauri/**'
- 'REMAPPING/meridian-desktop/src-tauri/**'
web:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'web/**'
- 'REMAPPING/meridian-web/**'
- 'pnpm-lock.yaml'
admin-web:
- 'admin-web/**'
# `admin-web/src/types.ts` mirrors the relay's admin response
- 'REMAPPING/meridian-admin-web/**'
# `REMAPPING/meridian-admin-web/src/types.ts` mirrors the relay's admin response
# contract, and the suite asserts that mirror. A relay-side change
# to the contract must therefore run this job, or the drift the
# specs exist to catch lands with the job skipped.
@ -79,7 +79,7 @@ jobs:
mobile:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'mobile/**'
- 'REMAPPING/meridian-mobile/**'
- 'scripts/mobile-release.sh'
- 'scripts/mobile-worktree-overrides.sh'
- 'scripts/mobile-worktree-clean.sh'
@ -280,7 +280,7 @@ jobs:
- uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: desktop/src-tauri
workspaces: REMAPPING/meridian-desktop/src-tauri
save-if: ${{ github.event_name != 'pull_request' }}
- name: Install Tauri dependencies (Linux)
env:
@ -346,8 +346,8 @@ jobs:
with:
name: desktop-e2e-artifacts
path: |
desktop/playwright-report
desktop/test-results
REMAPPING/meridian-desktop/playwright-report
REMAPPING/meridian-desktop/test-results
if-no-files-found: ignore
- name: Save pnpm store cache
if: github.event_name == 'push'
@ -384,7 +384,7 @@ jobs:
run: just desktop-install-ci
- name: Get Playwright version
id: pw-version
run: echo "version=$(cd desktop && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
run: echo "version=$(cd REMAPPING/meridian-desktop && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
- name: Restore Playwright browser cache
id: playwright-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -393,9 +393,9 @@ jobs:
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: cd desktop && pnpm exec playwright install chromium
run: cd REMAPPING/meridian-desktop && pnpm exec playwright install chromium
- name: Install Playwright system dependencies
run: cd desktop && pnpm exec playwright install-deps chromium
run: cd REMAPPING/meridian-desktop && pnpm exec playwright install-deps chromium
- name: Save Playwright browser cache
if: steps.playwright-cache.outputs.cache-hit != 'true' && github.event_name == 'push' && matrix.shard == 1
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -403,9 +403,9 @@ jobs:
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Desktop E2E build
run: pnpm -C desktop build:e2e
run: pnpm -C REMAPPING/meridian-desktop build:e2e
- name: Desktop smoke e2e
run: cd desktop && pnpm exec playwright test --project=smoke --shard=${{ matrix.shard }}/4
run: cd REMAPPING/meridian-desktop && pnpm exec playwright test --project=smoke --shard=${{ matrix.shard }}/4
- name: Summarize flaky tests
if: ${{ !cancelled() }}
run: node scripts/summarize-flaky-tests.mjs playwright-report.json "Desktop Smoke E2E (${{ matrix.shard }})"
@ -416,9 +416,9 @@ jobs:
with:
name: desktop-smoke-e2e-artifacts-${{ matrix.shard }}
path: |
desktop/playwright-report
desktop/playwright-report.json
desktop/test-results
REMAPPING/meridian-desktop/playwright-report
REMAPPING/meridian-desktop/playwright-report.json
REMAPPING/meridian-desktop/test-results
if-no-files-found: ignore
retention-days: 7
@ -473,7 +473,7 @@ jobs:
with:
workspaces: |
.
desktop/src-tauri
REMAPPING/meridian-desktop/src-tauri
save-if: ${{ github.event_name != 'pull_request' }}
- name: Install cargo-nextest
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
@ -553,7 +553,7 @@ jobs:
run: just desktop-install-ci
- name: Get Playwright version
id: pw-version
run: echo "version=$(cd desktop && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
run: echo "version=$(cd REMAPPING/meridian-desktop && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
- name: Restore Playwright browser cache
id: playwright-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -562,9 +562,9 @@ jobs:
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: cd desktop && pnpm exec playwright install chromium
run: cd REMAPPING/meridian-desktop && pnpm exec playwright install chromium
- name: Install Playwright system dependencies
run: cd desktop && pnpm exec playwright install-deps chromium
run: cd REMAPPING/meridian-desktop && pnpm exec playwright install-deps chromium
- name: Save Playwright browser cache
if: steps.playwright-cache.outputs.cache-hit != 'true' && github.event_name == 'push' && matrix.shard == 1
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -572,7 +572,7 @@ jobs:
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Desktop E2E build
run: pnpm -C desktop build:e2e
run: pnpm -C REMAPPING/meridian-desktop build:e2e
- name: Wait for integration services
run: |
wait_healthy() {
@ -665,7 +665,7 @@ jobs:
- name: Seed desktop e2e data
run: bash scripts/setup-desktop-test-data.sh
- name: Desktop relay-backed e2e
run: cd desktop && pnpm exec playwright test --project=integration --shard=${{ matrix.shard }}/2
run: cd REMAPPING/meridian-desktop && pnpm exec playwright test --project=integration --shard=${{ matrix.shard }}/2
- name: Summarize flaky tests
if: ${{ !cancelled() }}
run: node scripts/summarize-flaky-tests.mjs playwright-report.json "Desktop E2E Integration (${{ matrix.shard }}/2)"
@ -676,9 +676,9 @@ jobs:
with:
name: desktop-e2e-integration-artifacts-${{ matrix.shard }}
path: |
desktop/playwright-report
desktop/playwright-report.json
desktop/test-results
REMAPPING/meridian-desktop/playwright-report
REMAPPING/meridian-desktop/playwright-report.json
REMAPPING/meridian-desktop/test-results
/tmp/meridian-relay.log
if-no-files-found: ignore
retention-days: 7
@ -1135,10 +1135,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Admin web lint and typecheck
run: pnpm -C admin-web check
run: pnpm -C REMAPPING/meridian-admin-web check
- name: Get Playwright version
id: pw-version
run: echo "version=$(cd admin-web && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
run: echo "version=$(cd REMAPPING/meridian-admin-web && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
- name: Restore Playwright browser cache
id: playwright-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -1147,9 +1147,9 @@ jobs:
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: cd admin-web && pnpm exec playwright install chromium
run: cd REMAPPING/meridian-admin-web && pnpm exec playwright install chromium
- name: Install Playwright system dependencies
run: cd admin-web && pnpm exec playwright install-deps chromium
run: cd REMAPPING/meridian-admin-web && pnpm exec playwright install-deps chromium
- name: Save Playwright browser cache
if: steps.playwright-cache.outputs.cache-hit != 'true' && github.event_name == 'push'
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -1168,8 +1168,8 @@ jobs:
with:
name: admin-web-e2e-artifacts
path: |
admin-web/playwright-report
admin-web/test-results
REMAPPING/meridian-admin-web/playwright-report
REMAPPING/meridian-admin-web/test-results
if-no-files-found: ignore
retention-days: 7
- name: Save pnpm store cache
@ -1218,25 +1218,25 @@ jobs:
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
key: pub-${{ runner.os }}-${{ hashFiles('REMAPPING/meridian-mobile/pubspec.lock') }}
restore-keys: pub-${{ runner.os }}-
- name: Install dependencies
run: cd mobile && flutter pub get
run: cd REMAPPING/meridian-mobile && flutter pub get
- name: Save pub cache
if: always() && steps.pub-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
continue-on-error: true
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
key: pub-${{ runner.os }}-${{ hashFiles('REMAPPING/meridian-mobile/pubspec.lock') }}
- name: File size ratchet
run: node mobile/scripts/check-file-sizes.mjs
run: node REMAPPING/meridian-mobile/scripts/check-file-sizes.mjs
- name: Format check
run: cd mobile && dart format --output=none --set-exit-if-changed .
run: cd REMAPPING/meridian-mobile && dart format --output=none --set-exit-if-changed .
- name: Analyze
run: cd mobile && flutter analyze
run: cd REMAPPING/meridian-mobile && flutter analyze
- name: Test
run: cd mobile && flutter test
run: cd REMAPPING/meridian-mobile && flutter test
- name: Build Android debug APK
run: just mobile-build-android
@ -1267,7 +1267,7 @@ jobs:
# Fail if dead API token patterns reappear in desktop, mobile, docs, or config.
# Relay crates are excluded — they still use token auth internally.
PATTERNS='TokenScope|MintTokenResponse|hasApiToken|spr_tok_'
PATHS='desktop/src/ desktop/tests/ mobile/test/ mobile/lib/ .env.example'
PATHS='REMAPPING/meridian-desktop/src/ REMAPPING/meridian-desktop/tests/ REMAPPING/meridian-mobile/test/ REMAPPING/meridian-mobile/lib/ .env.example'
EXCLUDES='--exclude-dir=node_modules --exclude-dir=.dart_tool'
if grep -rn $EXCLUDES -E "$PATTERNS" $PATHS 2>/dev/null; then
echo "::error::Dead API token references found in client code. See above."
@ -1337,7 +1337,7 @@ jobs:
with:
workspaces: |
.
desktop/src-tauri
REMAPPING/meridian-desktop/src-tauri
key: windows-msvc
save-if: ${{ github.event_name != 'pull_request' }}
# Tauri validates externalBin at compile time, so the Tauri-crate steps
@ -1347,9 +1347,9 @@ jobs:
- name: Create sidecar placeholders
shell: bash
run: |
mkdir -p desktop/src-tauri/binaries
mkdir -p REMAPPING/meridian-desktop/src-tauri/binaries
for bin in meridian-acp meridian-agent meridian-dev-mcp git-credential-nostr meridian; do
touch "desktop/src-tauri/binaries/${bin}-${TARGET}.exe"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/${bin}-${TARGET}.exe"
done
- name: Clippy (workspace)
run: cargo clippy --workspace --all-targets --target $env:TARGET -- -D warnings
@ -1391,11 +1391,11 @@ jobs:
git log -1 --format=%s | grep -qx smoke
echo "Host bash resolved and functional; git commit round-trip passed"
- name: Check (Tauri crate)
run: cargo check --manifest-path desktop/src-tauri/Cargo.toml --target $env:TARGET
run: cargo check --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml --target $env:TARGET
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
- name: Test (Tauri crate)
run: cargo test --manifest-path desktop/src-tauri/Cargo.toml --target $env:TARGET
run: cargo test --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml --target $env:TARGET
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
@ -1412,19 +1412,19 @@ jobs:
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: desktop/src-tauri
workspaces: REMAPPING/meridian-desktop/src-tauri
save-if: ${{ github.event_name != 'pull_request' }}
- name: Install desktop dependencies
run: just desktop-install-ci
- name: Create sidecar placeholders
run: |
TARGET=$(rustc -vV | sed -n 's|host: ||p')
mkdir -p desktop/src-tauri/binaries
touch "desktop/src-tauri/binaries/meridian-acp-$TARGET"
touch "desktop/src-tauri/binaries/meridian-agent-$TARGET"
touch "desktop/src-tauri/binaries/meridian-dev-mcp-$TARGET"
touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
touch "desktop/src-tauri/binaries/meridian-$TARGET"
mkdir -p REMAPPING/meridian-desktop/src-tauri/binaries
touch "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-acp-$TARGET"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-agent-$TARGET"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-dev-mcp-$TARGET"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-$TARGET"
# Mesh rev is derived from Cargo.lock so a dependency bump needs no
# lockstep edit here; the cache key tracks it automatically.
- name: Resolve mesh-llm rev
@ -1447,7 +1447,7 @@ jobs:
MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }}
run: |
set -euo pipefail
cargo fetch --manifest-path desktop/src-tauri/Cargo.toml
cargo fetch --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml
SHORT="$MESH_REV_SHORT"
MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1)
if [[ -z "$MESH_ROOT" ]]; then
@ -1466,7 +1466,7 @@ jobs:
path: ${{ github.workspace }}/.cache/mesh-llama
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
- name: Build Tauri app
run: cd desktop && pnpm tauri build
run: cd REMAPPING/meridian-desktop && pnpm tauri build
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
MACOSX_DEPLOYMENT_TARGET: "10.15"

View file

@ -16,8 +16,8 @@ on:
- "Cargo.lock"
- "rust-toolchain.toml"
- "hermit.hcl"
- "desktop/tests/control-plane/**"
- "desktop/package.json"
- "REMAPPING/meridian-desktop/tests/control-plane/**"
- "REMAPPING/meridian-desktop/package.json"
- "pnpm-lock.yaml"
- ".github/workflows/control-plane.yml"
push:
@ -33,8 +33,8 @@ on:
- "Cargo.lock"
- "rust-toolchain.toml"
- "hermit.hcl"
- "desktop/tests/control-plane/**"
- "desktop/package.json"
- "REMAPPING/meridian-desktop/tests/control-plane/**"
- "REMAPPING/meridian-desktop/package.json"
- "pnpm-lock.yaml"
- ".github/workflows/control-plane.yml"
@ -157,9 +157,9 @@ jobs:
- name: Install browser test dependencies
run: |
just desktop-install-ci
pnpm -C desktop exec playwright install --with-deps chromium
pnpm -C REMAPPING/meridian-desktop exec playwright install --with-deps chromium
- name: Community registration browser E2E
run: pnpm -C desktop exec playwright test --config tests/control-plane/playwright.config.mjs
run: pnpm -C REMAPPING/meridian-desktop exec playwright test --config tests/control-plane/playwright.config.mjs
env:
CONTROL_PLANE_URL: http://127.0.0.1:8090
- name: Upload failure artifacts
@ -168,7 +168,7 @@ jobs:
with:
name: control-plane-browser-e2e
path: |
desktop/test-results
REMAPPING/meridian-desktop/test-results
/tmp/control-plane-e2e.log
/tmp/control-plane-e2e-relay.log
if-no-files-found: ignore

View file

@ -53,7 +53,7 @@ on:
- "Cargo.lock"
- "rust-toolchain.toml"
- "crates/**"
- "web/**"
- "REMAPPING/meridian-web/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"

View file

@ -92,7 +92,7 @@ jobs:
with:
workspaces: |
.
desktop/src-tauri
REMAPPING/meridian-desktop/src-tauri
shared-key: linux-canary-release
- name: Install appimagetool
@ -138,7 +138,7 @@ jobs:
id: version
run: |
set -euo pipefail
BASE_VERSION=$(node -p "require('./desktop/package.json').version")
BASE_VERSION=$(node -p "require('./REMAPPING/meridian-desktop/package.json').version")
if ! [[ "$BASE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Desktop version '$BASE_VERSION' is not semver"
exit 1
@ -151,12 +151,12 @@ jobs:
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate non-updating bundle config
run: |
cat > desktop/src-tauri/tauri.canary.conf.json <<'JSON'
cat > REMAPPING/meridian-desktop/src-tauri/tauri.canary.conf.json <<'JSON'
{
"bundle": {
"createUpdaterArtifacts": false
@ -170,7 +170,7 @@ jobs:
./scripts/bundle-sidecars.sh
- name: Build Linux Tauri app
run: cd desktop && pnpm tauri build --ci --bundles deb,appimage --config src-tauri/tauri.canary.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --ci --bundles deb,appimage --config src-tauri/tauri.canary.conf.json
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
@ -179,7 +179,7 @@ jobs:
# re-signing when absent, so no signing env vars are needed here.
# Repacking still runs, removing the Mesa/GLib/GStreamer conflict libs.
run: |
mapfile -t APPIMAGES < <(find desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f)
mapfile -t APPIMAGES < <(find REMAPPING/meridian-desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f)
if [[ ${#APPIMAGES[@]} -eq 0 ]]; then
echo "::error::No AppImage found to post-process"
exit 1
@ -188,7 +188,7 @@ jobs:
echo "::error::Expected exactly one AppImage, found ${#APPIMAGES[@]}: ${APPIMAGES[*]}"
exit 1
fi
bash desktop/scripts/fix-appimage.sh "${APPIMAGES[0]}"
bash REMAPPING/meridian-desktop/scripts/fix-appimage.sh "${APPIMAGES[0]}"
- name: Save pnpm store cache
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
@ -200,7 +200,7 @@ jobs:
id: artifacts
run: |
set -euo pipefail
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/release/bundle"
DEB=$(find "$BUNDLE_DIR/deb" -name '*.deb' -type f | head -1)
if [[ -z "$DEB" ]]; then

View file

@ -123,11 +123,11 @@ jobs:
- name: Patch version
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate release config
run: cd desktop && node scripts/build-release-config.mjs
run: cd REMAPPING/meridian-desktop && node scripts/build-release-config.mjs
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -158,7 +158,7 @@ jobs:
MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }}
run: |
set -euo pipefail
cargo fetch --manifest-path desktop/src-tauri/Cargo.toml
cargo fetch --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml
SHORT="$MESH_REV_SHORT"
MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1)
if [[ -z "$MESH_ROOT" ]]; then
@ -178,7 +178,7 @@ jobs:
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
- name: Build unsigned Tauri app
run: cd desktop && pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.release.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.release.conf.json
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -195,7 +195,7 @@ jobs:
- name: Locate unsigned DMG
id: unsigned
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/release/bundle"
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
if [[ -z "$DMG" ]]; then
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
@ -206,11 +206,11 @@ jobs:
- name: Set DMG Finder label text size
env:
DMG_PATH: ${{ steps.unsigned.outputs.dmg }}
run: desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
run: REMAPPING/meridian-desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
# mdx-ios-codesign-helper discovers this file by its exact lowercase basename.
- name: Stage signing entitlements
run: cp desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
run: cp REMAPPING/meridian-desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
- name: Codesign and Notarize
id: codesign
@ -231,7 +231,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/release/bundle"
APP_DIR="${BUNDLE_DIR}/macos"
# Replace unsigned DMG with the signed/notarized one.
@ -248,21 +248,21 @@ jobs:
rm -f "${APP_DIR}/Meridian.app.tar.gz" "${APP_DIR}/Meridian.app.tar.gz.sig"
(cd "$APP_DIR" && tar -czf Meridian.app.tar.gz Meridian.app)
TARBALL_ABS="$(pwd)/${APP_DIR}/Meridian.app.tar.gz"
(cd desktop && pnpm tauri signer sign "$TARBALL_ABS")
(cd REMAPPING/meridian-desktop && pnpm tauri signer sign "$TARBALL_ABS")
- name: Verify code signature
run: |
codesign --verify --deep --strict --verbose=2 \
desktop/src-tauri/target/release/bundle/macos/Meridian.app
REMAPPING/meridian-desktop/src-tauri/target/release/bundle/macos/Meridian.app
spctl --assess --type execute --verbose=4 \
desktop/src-tauri/target/release/bundle/macos/Meridian.app
desktop/scripts/verify-macos-entitlements.sh \
desktop/src-tauri/target/release/bundle/macos/Meridian.app
REMAPPING/meridian-desktop/src-tauri/target/release/bundle/macos/Meridian.app
REMAPPING/meridian-desktop/scripts/verify-macos-entitlements.sh \
REMAPPING/meridian-desktop/src-tauri/target/release/bundle/macos/Meridian.app
- name: Locate build artifacts
id: artifacts
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/release/bundle"
# Find the DMG (Tauri names it Meridian_<version>_<arch>.dmg)
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
@ -342,11 +342,11 @@ jobs:
- name: Patch version
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate release config
run: cd desktop && node scripts/build-release-config.mjs
run: cd REMAPPING/meridian-desktop && node scripts/build-release-config.mjs
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -357,7 +357,7 @@ jobs:
./scripts/bundle-sidecars.sh "$TARGET"
- name: Build unsigned Tauri app
run: cd desktop && pnpm tauri build --verbose --no-sign --target "$TARGET" --config src-tauri/tauri.release.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --verbose --no-sign --target "$TARGET" --config src-tauri/tauri.release.conf.json
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -371,7 +371,7 @@ jobs:
- name: Locate unsigned DMG
id: unsigned
run: |
BUNDLE_DIR="desktop/src-tauri/target/${TARGET}/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle"
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
if [[ -z "$DMG" ]]; then
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
@ -382,11 +382,11 @@ jobs:
- name: Set DMG Finder label text size
env:
DMG_PATH: ${{ steps.unsigned.outputs.dmg }}
run: desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
run: REMAPPING/meridian-desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
# mdx-ios-codesign-helper discovers this file by its exact lowercase basename.
- name: Stage signing entitlements
run: cp desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
run: cp REMAPPING/meridian-desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
- name: Codesign and Notarize
id: codesign
@ -407,7 +407,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
APP_DIR="desktop/src-tauri/target/${TARGET}/release/bundle/macos"
APP_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle/macos"
# Replace the unsigned DMG with the signed/notarized one.
cp "$SIGNED_DMG" "$UNSIGNED_DMG"
@ -423,19 +423,19 @@ jobs:
rm -f "${APP_DIR}/Meridian.app.tar.gz" "${APP_DIR}/Meridian.app.tar.gz.sig"
(cd "$APP_DIR" && tar -czf Meridian.app.tar.gz Meridian.app)
TARBALL_ABS="$(pwd)/${APP_DIR}/Meridian.app.tar.gz"
(cd desktop && pnpm tauri signer sign "$TARBALL_ABS")
(cd REMAPPING/meridian-desktop && pnpm tauri signer sign "$TARBALL_ABS")
- name: Verify code signature
run: |
APP_DIR="desktop/src-tauri/target/${TARGET}/release/bundle/macos/Meridian.app"
APP_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle/macos/Meridian.app"
codesign --verify --deep --strict --verbose=2 "$APP_DIR"
spctl --assess --type execute --verbose=4 "$APP_DIR"
desktop/scripts/verify-macos-entitlements.sh "$APP_DIR"
REMAPPING/meridian-desktop/scripts/verify-macos-entitlements.sh "$APP_DIR"
- name: Locate updater archive
id: artifacts
run: |
BUNDLE_DIR="desktop/src-tauri/target/${TARGET}/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle"
ARCHIVE=$(find "$BUNDLE_DIR/macos" -name '*.tar.gz' ! -name '*.sig' -type f | head -1)
SIG="${ARCHIVE}.sig"
@ -561,7 +561,7 @@ jobs:
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: desktop/src-tauri
workspaces: REMAPPING/meridian-desktop/src-tauri
lookup-only: true
- name: Install appimagetool
@ -606,7 +606,7 @@ jobs:
env:
VERSION: ${{ needs.setup.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Build sidecars
@ -615,13 +615,13 @@ jobs:
./scripts/bundle-sidecars.sh
- name: Generate release config
run: cd desktop && node scripts/build-release-config.mjs
run: cd REMAPPING/meridian-desktop && node scripts/build-release-config.mjs
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
- name: Build Linux Tauri app
run: cd desktop && pnpm tauri build --verbose --ci --bundles deb,appimage --config src-tauri/tauri.release.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --verbose --ci --bundles deb,appimage --config src-tauri/tauri.release.conf.json
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -631,7 +631,7 @@ jobs:
- name: Fix AppImage (remove infra libs, shim host GStreamer)
run: |
mapfile -t APPIMAGES < <(find desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f)
mapfile -t APPIMAGES < <(find REMAPPING/meridian-desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f)
if [[ ${#APPIMAGES[@]} -eq 0 ]]; then
echo "::error::No AppImage found to post-process"
exit 1
@ -640,7 +640,7 @@ jobs:
echo "::error::Expected exactly one AppImage, found ${#APPIMAGES[@]}: ${APPIMAGES[*]}"
exit 1
fi
bash desktop/scripts/fix-appimage.sh "${APPIMAGES[0]}"
bash REMAPPING/meridian-desktop/scripts/fix-appimage.sh "${APPIMAGES[0]}"
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
@ -648,7 +648,7 @@ jobs:
- name: Locate Linux build artifacts
id: linux-artifacts
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/release/bundle"
DEB=$(find "$BUNDLE_DIR/deb" -name '*.deb' -type f | head -1)
if [[ -z "$DEB" ]]; then
@ -760,12 +760,12 @@ jobs:
- name: Patch version
shell: bash
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate release config
shell: bash
run: cd desktop && node scripts/build-release-config.mjs
run: cd REMAPPING/meridian-desktop && node scripts/build-release-config.mjs
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -778,7 +778,7 @@ jobs:
- name: Build Windows NSIS installer (unsigned)
shell: bash
run: cd desktop && pnpm tauri build --verbose --target "$TARGET" --bundles nsis --config src-tauri/tauri.release.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --verbose --target "$TARGET" --bundles nsis --config src-tauri/tauri.release.conf.json
env:
MERIDIAN_UPDATER_PUBLIC_KEY: ${{ secrets.MERIDIAN_UPDATER_PUBLIC_KEY }}
MERIDIAN_UPDATER_ENDPOINT: https://git.office.ilab.zone/RAID/R2D2-MERIDIAN/releases/download/meridian-desktop-latest/latest.json
@ -790,7 +790,7 @@ jobs:
id: artifacts
shell: bash
run: |
BUNDLE_DIR="desktop/src-tauri/target/${TARGET}/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle"
# Find the NSIS installer .exe
EXE=$(find "$BUNDLE_DIR/nsis" -name '*.exe' -type f | head -1)
@ -974,7 +974,7 @@ jobs:
add_triple "$RESULT_WIN" windows-x86_64 "$ARCHIVE_WIN"
[ "${#TRIPLES[@]}" -ge 3 ] || { echo "::error::too few platforms (${#TRIPLES[@]})"; exit 1; }
bash desktop/scripts/generate-oss-latest-json.sh "$VERSION" "${TRIPLES[@]}" > latest.json
bash REMAPPING/meridian-desktop/scripts/generate-oss-latest-json.sh "$VERSION" "${TRIPLES[@]}" > latest.json
cat latest.json
- name: Upload latest.json to rolling release

View file

@ -41,7 +41,7 @@ jobs:
with:
workspaces: |
.
desktop/src-tauri
REMAPPING/meridian-desktop/src-tauri
shared-key: macos-canary-release
- name: Get pnpm store directory
@ -62,7 +62,7 @@ jobs:
id: version
run: |
set -euo pipefail
BASE_VERSION=$(node -p "require('./desktop/package.json').version")
BASE_VERSION=$(node -p "require('./REMAPPING/meridian-desktop/package.json').version")
if ! [[ "$BASE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Desktop version '$BASE_VERSION' is not semver"
exit 1
@ -75,12 +75,12 @@ jobs:
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate non-updating bundle config
run: |
cat > desktop/src-tauri/tauri.canary.conf.json <<'JSON'
cat > REMAPPING/meridian-desktop/src-tauri/tauri.canary.conf.json <<'JSON'
{
"bundle": {
"macOS": {
@ -119,7 +119,7 @@ jobs:
MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }}
run: |
set -euo pipefail
cargo fetch --manifest-path desktop/src-tauri/Cargo.toml
cargo fetch --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml
MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$MESH_REV_SHORT" -type d -name "$MESH_REV_SHORT" | head -1)
if [[ -z "$MESH_ROOT" ]]; then
echo "::error::mesh-llm checkout for $MESH_REV_SHORT not found after cargo fetch"
@ -139,7 +139,7 @@ jobs:
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
- name: Build unsigned Tauri app
run: cd desktop && pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.canary.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.canary.conf.json
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
MACOSX_DEPLOYMENT_TARGET: "10.15"
@ -152,7 +152,7 @@ jobs:
- name: Locate unsigned DMG
id: unsigned
run: |
DMG=$(find desktop/src-tauri/target/release/bundle/dmg -name '*.dmg' -type f | head -1)
DMG=$(find REMAPPING/meridian-desktop/src-tauri/target/release/bundle/dmg -name '*.dmg' -type f | head -1)
if [[ -z "$DMG" ]]; then
echo "::error::No DMG found"
exit 1
@ -162,11 +162,11 @@ jobs:
- name: Set DMG Finder label text size
env:
DMG_PATH: ${{ steps.unsigned.outputs.dmg }}
run: desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
run: REMAPPING/meridian-desktop/scripts/set-dmg-finder-text-size.sh "$DMG_PATH" 14
# mdx-ios-codesign-helper discovers this file by its exact lowercase basename.
- name: Stage signing entitlements
run: cp desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
run: cp REMAPPING/meridian-desktop/src-tauri/Entitlements.plist "${RUNNER_TEMP}/entitlements.plist"
- name: Codesign and notarize
id: codesign
@ -188,7 +188,7 @@ jobs:
ditto -x -k "$SIGNED_APP_ZIP" "$EXTRACT_DIR"
codesign --verify --deep --strict --verbose=2 "$EXTRACT_DIR/Meridian.app"
spctl --assess --type execute --verbose=4 "$EXTRACT_DIR/Meridian.app"
desktop/scripts/verify-macos-entitlements.sh "$EXTRACT_DIR/Meridian.app"
REMAPPING/meridian-desktop/scripts/verify-macos-entitlements.sh "$EXTRACT_DIR/Meridian.app"
- name: Stage signed DMG
id: artifact

View file

@ -54,7 +54,7 @@ jobs:
with:
workspaces: |
.
desktop/src-tauri
REMAPPING/meridian-desktop/src-tauri
shared-key: windows-canary-release
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@ -91,7 +91,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
BASE_VERSION=$(node -p "require('./desktop/package.json').version")
BASE_VERSION=$(node -p "require('./REMAPPING/meridian-desktop/package.json').version")
if ! [[ "$BASE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Desktop version '$BASE_VERSION' is not semver"
exit 1
@ -105,13 +105,13 @@ jobs:
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd REMAPPING/meridian-desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate non-updating bundle config
shell: bash
run: |
cat > desktop/src-tauri/tauri.canary.conf.json <<'JSON'
cat > REMAPPING/meridian-desktop/src-tauri/tauri.canary.conf.json <<'JSON'
{
"bundle": {
"createUpdaterArtifacts": false
@ -127,7 +127,7 @@ jobs:
- name: Build Windows NSIS installer (unsigned)
shell: bash
run: cd desktop && pnpm tauri build --target "$TARGET" --bundles nsis --config src-tauri/tauri.canary.conf.json
run: cd REMAPPING/meridian-desktop && pnpm tauri build --target "$TARGET" --bundles nsis --config src-tauri/tauri.canary.conf.json
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
@ -136,7 +136,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
BUNDLE_DIR="desktop/src-tauri/target/${TARGET}/release/bundle"
BUNDLE_DIR="REMAPPING/meridian-desktop/src-tauri/target/${TARGET}/release/bundle"
EXE=$(find "$BUNDLE_DIR/nsis" -name '*.exe' -type f | head -1)
if [[ -z "$EXE" ]]; then
echo "::error::No NSIS installer found in $BUNDLE_DIR/nsis"

2
.gitignore vendored
View file

@ -1,7 +1,7 @@
# Build artifacts
/target/
/dist/
/admin-web/dist/
/REMAPPING/meridian-admin-web/dist/
# lefthook-generated hook scripts (machine-specific)
.hooks/

View file

@ -132,7 +132,7 @@ Three load-bearing paths. `packages/protobufs/` is generated from `meshtastic/pr
- Path: `.settings/reference-code/MQTT-Load-Test`
- Entry points: `deploy/`, `docs/`, `loadtest/`, `node-red/`, `payloads/`, `refactor_loadtest/`, `reports/`, `rust-consumer/`, `rust-mqtt-core/`, `rust-producer/`
- Own docs: `README.md` (upstream — never binding here)
- Cited by: _(nothing in the tracked tree)_
- Cited by: `.beads/issues.jsonl`, `.settings/features/feature-machine-data-feeds.md`
<!-- stellar-ref:MQTT-Load-Test:end -->
Why we keep it: Broker-displacement harness reference for `TASKS.md`. `loadtest/` and `payloads/` are the shapes to copy when `meridian-ctg` gets its size sweep.

File diff suppressed because it is too large Load diff

View file

@ -76,9 +76,9 @@ crates/
meridian-test-db # Test-DB resolution + the fail-closed disposability guard
meridian-harness # All-in-one harness bundling ACP, agent, and dev MCP
desktop/ # Tauri 2 + React 19 desktop app
web/ # Browser web client (repo browser, served by the relay)
mobile/ # Flutter mobile app
REMAPPING/meridian-desktop/ # Tauri 2 + React 19 desktop app
REMAPPING/meridian-web/ # Browser web client (repo browser, served by the relay)
REMAPPING/meridian-mobile/ # Flutter mobile app
migrations/ # SQL migrations (auto-applied on relay startup)
scripts/ # Dev tooling
examples/ # Samples; countdown-bot is a Cargo workspace member
@ -217,7 +217,7 @@ every member is keyed off a port:
handed back to clients.
A GUI-launched `.app` inherits no shell environment, so a packaged build reads
the compile-time bake instead (`option_env!` in `desktop/src-tauri/src/mns.rs`).
the compile-time bake instead (`option_env!` in `REMAPPING/meridian-desktop/src-tauri/src/mns.rs`).
`./run.sh bundle` resolves ports before building, so the running bundle's
endpoint matches this allocation; a bundle built any other way will not.
@ -228,7 +228,7 @@ them.** `--mesh` compiles in shared compute (`mesh-llm`) on `desktop`, `all` and
It is off by default for cost (~420 crates plus a llama.cpp runtime build), but
the cost of *omitting* it is not silent: Settings → Compute reports "Not
included in this build", because every `mesh_*` command is the stub in
`desktop/src-tauri/src/mesh_llm_stubs.rs`. Before `--mesh` existed, run.sh had
`REMAPPING/meridian-desktop/src-tauri/src/mesh_llm_stubs.rs`. Before `--mesh` existed, run.sh had
no way to build the feature at all while being the documented headline command,
and the only recipe that did (`just desktop-release-build`) staged **zero-byte**
sidecars — so neither path produced a working mesh-capable bundle.
@ -722,7 +722,7 @@ E2E tests live in `crates/meridian-test-client/tests/`:
- `e2e_media_extended.rs` — extended media scenarios
- `e2e_nostr_interop.rs` — Nostr interop (NIP-50 search, NIP-10 threads, NIP-17 gift wraps)
Desktop E2E: `cd desktop && pnpm exec playwright test`
Desktop E2E: `cd REMAPPING/meridian-desktop && pnpm exec playwright test`
See [TESTING.md](TESTING.md) for the full multi-agent E2E guide.
@ -861,13 +861,13 @@ Branch cleanup when fully done: `git push origin --delete agent-screenshots/<use
When screenshots need seeded state, live messages, or UI interaction before
capture, write a Playwright spec instead of using `just desktop-screenshot`.
Add specs to `desktop/tests/e2e/` and register them in `playwright.config.ts`
Add specs to `REMAPPING/meridian-desktop/tests/e2e/` and register them in `playwright.config.ts`
(`smoke` project `testMatch`). Every test calls `installMockBridge(page)` for
mock Tauri IPC. Mock pubkey, channel names, and UUIDs live in `e2eBridge.ts`.
**Always build with `pnpm build:e2e`, never `pnpm run build`.** The mock Tauri
bridge is compiled in only for `--mode e2e` (see `installE2eBridgeIfConfigured`
in `desktop/src/main.tsx`). A plain `pnpm run build` strips it, so
in `REMAPPING/meridian-desktop/src/main.tsx`). A plain `pnpm run build` strips it, so
`window.__TAURI_INTERNALS__` is never defined and **every** mock-mode spec fails
with `Cannot read properties of undefined (reading 'invoke')` — the app renders
"Community connection failed" instead of the UI under test. That looks exactly
@ -949,7 +949,7 @@ description. See [PR #803](https://github.com/block/buzz/pull/803).
2. **Relay queries must specify `kinds`** — omitting `kinds` triggers the p-gate (403). Always include explicit kind filters.
3. **`messages search` must include `--kinds`** — an open-ended search (no kinds) hits the relay p-gate and returns 403. Pass at least `--kinds 9,45001,45003` to scope the query.
4. **Worktrees: `cd` in the same command** — shell CWD doesn't persist between tool calls. Use `cd /path && cargo build` as one command.
5. **Desktop crate excluded from root workspace** — `cargo test` at repo root does NOT run desktop tests. Use `cargo test --manifest-path desktop/src-tauri/Cargo.toml` explicitly.
5. **Desktop crate excluded from root workspace** — `cargo test` at repo root does NOT run desktop tests. Use `cargo test --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml` explicitly.
6. **Desktop Tauri fmt fails in worktrees and blocks commits** — the pre-commit hook runs `just desktop-tauri-fmt`, which fails in git worktrees because `cargo fmt` resolves workspace paths relative to the worktree root. Run `just desktop-tauri-fmt` from the main checkout to apply the fix, then re-stage and commit. CI is unaffected.
7. **React render perf: `React.memo` is all-or-nothing** — it only skips a re-render when _every_ prop is reference-stable; one unstable prop (inline arrow/JSX, or a hook returning a fresh `{}`/`[]`/`Map` each render) defeats it. Two repeat offenders: (a) React Query results (`useMutation`/`useQuery`) are a **new object each render** — depend on the stable method (`mutation.mutateAsync`), not the object; (b) derived `Map`/array state that recomputes on a version bump — wrap in a content-equality ref cache (`shared/hooks/useStableReference.ts`). When chasing interaction lag, **measure with DevTools closed and no perf probes** (an open Web Inspector + per-keystroke `console.log` inflate the numbers), and isolate by removing one suspect at a time rather than guessing.
@ -958,7 +958,7 @@ description. See [PR #803](https://github.com/block/buzz/pull/803).
## Desktop App
The desktop app is Tauri 2 + React 19 + Vite + Tailwind CSS. Features are
organized under `desktop/src/features/`. Biome handles linting and formatting.
organized under `REMAPPING/meridian-desktop/src/features/`. Biome handles linting and formatting.
```bash
just desktop-dev # web-only dev server (faster iteration)
@ -968,7 +968,7 @@ just dev # full Tauri app with native shell
### Text sizing & zoom (use rem, never px)
The desktop app implements Cmd +/- zoom by scaling the root `<html>`
font-size (`desktop/src/app/useWebviewZoomShortcuts.ts`) and pinning the native
font-size (`REMAPPING/meridian-desktop/src/app/useWebviewZoomShortcuts.ts`) and pinning the native
webview zoom. **Only rem-based text scales with zoom — hardcoded px text sizes
are frozen.**
@ -980,7 +980,7 @@ px:
surrounding timeline elements (timestamps, system rows, code, reactions) are
deliberate steps on that same stock ramp.
- ✅ The `text-2xs` (0.6875rem / 11px) and `text-3xs` (0.5rem / 8px) meta-text
tokens (in `desktop/tailwind.config.js` under `theme.extend.fontSize`) for the
tokens (in `REMAPPING/meridian-desktop/tailwind.config.js` under `theme.extend.fontSize`) for the
sub-`text-xs` ramp — timestamps, count badges, tracking labels, tiny glyphs.
These replaced the dozens of arbitrary `text-[…rem]` literals that had drifted
apart pixel-by-pixel; keep meta text on these two tokens, not new arbitrary
@ -992,9 +992,9 @@ px:
Prefer stock tokens — they're rem and zoom-safe. Only if a design genuinely
needs a size the stock/`2xs`/`3xs` scale can't express should you **add a
rem-based token** (in `desktop/tailwind.config.js` under `theme.extend.fontSize`)
rem-based token** (in `REMAPPING/meridian-desktop/tailwind.config.js` under `theme.extend.fontSize`)
rather than an arbitrary literal. A CI guard (`pnpm check:px-text`, in
`desktop/scripts/check-px-text.mjs`) scans all of `desktop/src` and fails on any
`REMAPPING/meridian-desktop/scripts/check-px-text.mjs`) scans all of `REMAPPING/meridian-desktop/src` and fails on any
new arbitrary text-size literal — px **or** rem/em. Genuinely decorative glyphs
(e.g. the `text-[6rem]` avatar emoji) are allowlisted by `path:line` in that
script.
@ -1010,7 +1010,7 @@ community-scoped subtree to unmount and remount with fresh state.
clears React state (useState, useRef, context). Module-level variables (Maps,
class instances, cached promises) survive across remounts. Every community-scoped
singleton needs a reset function wired into `resetCommunityState()` in
`desktop/src/features/communities/useCommunityInit.ts`.
`REMAPPING/meridian-desktop/src/features/communities/useCommunityInit.ts`.
Current singletons that are reset on relay boundary changes (same-relay
reconnects preserve pending avatar verification work):
@ -1037,15 +1037,15 @@ Failure to do so causes data from the old community to leak into the new one.
Key files:
- `desktop/src/app/App.tsx` — community key, init gate, remount boundary
- `desktop/src/features/communities/useCommunityInit.ts` — `resetCommunityState()`, applies config to Tauri backend
- `desktop/src/main.tsx` — provider hierarchy (`QueryClientProvider` > `App`)
- `REMAPPING/meridian-desktop/src/app/App.tsx` — community key, init gate, remount boundary
- `REMAPPING/meridian-desktop/src/features/communities/useCommunityInit.ts` — `resetCommunityState()`, applies config to Tauri backend
- `REMAPPING/meridian-desktop/src/main.tsx` — provider hierarchy (`QueryClientProvider` > `App`)
---
## Mobile App (Flutter)
The mobile app lives in `mobile/` — a Flutter app using Riverpod + Hooks.
The mobile app lives in `REMAPPING/meridian-mobile/` — a Flutter app using Riverpod + Hooks.
### Architecture
@ -1053,7 +1053,7 @@ The mobile app lives in `mobile/` — a Flutter app using Riverpod + Hooks.
- **Theme:** Catppuccin Latte (light) / Macchiato (dark) — matches desktop
- **Features:** Isolated under `lib/features/`, shared code in `lib/shared/`
- **Nostr models:** `lib/shared/relay/nostr_models.dart` — event kinds must
stay in sync with `desktop/src/shared/constants/kinds.ts`
stay in sync with `REMAPPING/meridian-desktop/src/shared/constants/kinds.ts`
### Rules
@ -1068,7 +1068,7 @@ The mobile app lives in `mobile/` — a Flutter app using Riverpod + Hooks.
- **Keep widgets small and composable.** One public widget per file; push
private sub-widgets (`_Foo`) into sibling `part` files under a
`<page>/` folder rather than growing the page file. Hard ceiling:
**1000 lines/file**, enforced by `mobile/scripts/check-file-sizes.mjs` via
**1000 lines/file**, enforced by `REMAPPING/meridian-mobile/scripts/check-file-sizes.mjs` via
`just mobile-check` (runs in `just check` + pre-push, mirroring desktop/web).
If the guard trips, **split the file — never bump the limit or add an
override to slip under it.**
@ -1079,7 +1079,7 @@ The mobile app lives in `mobile/` — a Flutter app using Riverpod + Hooks.
### Quality Checks
```bash
cd mobile
cd REMAPPING/meridian-mobile
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test
@ -1099,7 +1099,7 @@ mobile-build-android`) give the debug build a per-worktree app identifier
`scripts/mobile-worktree-overrides.sh`, so builds from multiple worktrees
install side by side. Release builds are unaffected. `just mobile-clean`
removes stale worktree-suffixed installs from simulators/emulators. See
[mobile/README.md](mobile/README.md) for direct Xcode / Android Studio
[REMAPPING/meridian-mobile/README.md](REMAPPING/meridian-mobile/README.md) for direct Xcode / Android Studio
usage.
### Testing Conventions
@ -1239,16 +1239,13 @@ your target before editing.
| Child | Covers |
| --- | --- |
| `crates/AGENTS.md` | The Rust workspace — relay, protocol libraries, agent surface, CLI, tooling crates. Nested: `meridian-core`, `meridian-relay`, `meridian-db`, `meridian-acp`, `meridian-agent`, `meridian-cli`, `meridian-test-client` |
| `desktop/AGENTS.md` | Tauri 2 + React 19 desktop app. Nested: `src/features/` (→ `features/agents/`), `src-tauri/`, `tests/` |
| `mobile/AGENTS.md` | Flutter app — Riverpod + hooks rules, file ceiling, safe `flutter` commands |
| `web/AGENTS.md` | Browser web client (repo browser + invite accept) served by the relay |
| `admin-web/AGENTS.md` | Read-only operator dashboard and its relay admin API contract |
| `migrations/AGENTS.md` | Forward-only Postgres schema; checksum-frozen files, partition/fence invariants |
| `docs/AGENTS.md` | Repo-local NIPs, TLA+/Tamarin specs, formal NIP-PL models, operator guides |
| `deploy/AGENTS.md` | Helm charts and Compose stack; chart values/schema/test contract |
| `scripts/AGENTS.md` | Dev tooling, shared lint guard cores, release/contract scripts, operator SQL |
| `benchmarks/AGENTS.md` | `harbor-meridian-orchestra` multi-agent benchmark (the repo's only Python toolchain) |
| `.github/AGENTS.md` | Required CI checks, release automation, DCO, action pinning, filter/lefthook parity |
| `REMAPPING/AGENTS.md` | IHLC delivery grammar — one directory per deployable (infra Dockerfiles/config + client source for desktop/web/mobile/admin-web), GitLab pipelines, Compose, OpenResty edge, Keycloak realm. Nested: `meridian-desktop/`, `meridian-web/`, `meridian-mobile/`, `meridian-admin-web/` |
| `.settings/AGENTS.md` | Working docs — feature/parity specs, the rebrand record, GOAT/EFDI distillations, the `stellar/` chain snapshot and reference-code index, and the gitignored third-party checkouts. Nested `AGENTS.md` under `.settings/reference-code/` are upstream docs and **not** part of this chain |
### Owned by this file
@ -1272,7 +1269,7 @@ Not delegated to any child — change these under the root contract:
workspace `Cargo.toml`, `crates/meridian-persona/Cargo.toml`, the
`meridian-desktop` clarification in `deny.toml`, and
`deploy/charts/meridian/Chart.yaml`. Third-party attributions under
`desktop/*/harness-logos/CREDITS.md` carry upstream terms and are not ours
`REMAPPING/meridian-desktop/*/harness-logos/CREDITS.md` carry upstream terms and are not ours
to change
- `TASKS.md` — the 10-week decentralized-relay program plan (MQTT/TBMQ bus
displacement). Executes the ratified council call in
@ -1284,6 +1281,11 @@ Not delegated to any child — change these under the root contract:
- `skills/` — canonical project skill suite (routing rules in
`skills/README.md`), symlinked into `.claude/skills/`, `.codex/skills/`, and
`.agents/skills/` for runtime discovery; gated by `just check-skills`
- `REMAPPING/` is **not** owned here — it is a child of this doc with its own
`AGENTS.md`, listed in the index above. It arrived as an embedded clone of the
IHLC GitLab project (`gitlab.ilab.zone/…/team-alpha/meridian`); that nested
`.git` has since been removed, so it is now ordinary untracked content of this
repo and commits there land on `git.office.ilab.zone` like any other path
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:7510c1e2 -->
## Beads Issue Tracker

View file

@ -694,7 +694,7 @@
- chore(deps): pin dependencies ([#1162](https://github.com/block/buzz/pull/1162)) ([`4b01040af`](https://github.com/block/buzz/commit/4b01040afb3cc778f3a2a1d8f89af37352b2ef35))
- feat(desktop): replace placeholder icon with actual app icon on welcome screen ([#1527](https://github.com/block/buzz/pull/1527)) ([`5b88e5956`](https://github.com/block/buzz/commit/5b88e595657cf4ffc740e30b187439fe53547331))
- feat(desktop): enable native spellcheck in message composer ([#1515](https://github.com/block/buzz/pull/1515)) ([`a1b5983e5`](https://github.com/block/buzz/commit/a1b5983e5400a6bd9f4c8056086de616931e33b4))
- fix: let agent owners delete their agent's messages (relay kind:5 + desktop/mobile UX) ([#1519](https://github.com/block/buzz/pull/1519)) ([`642800548`](https://github.com/block/buzz/commit/6428005487f0690019dc27449a6a52cc29cc6479))
- fix: let agent owners delete their agent's messages (relay kind:5 + REMAPPING/meridian-desktop/mobile UX) ([#1519](https://github.com/block/buzz/pull/1519)) ([`642800548`](https://github.com/block/buzz/commit/6428005487f0690019dc27449a6a52cc29cc6479))
- chore(desktop): bring ChannelScreen back under the size gate ([#1520](https://github.com/block/buzz/pull/1520)) ([`8174f2b0a`](https://github.com/block/buzz/commit/8174f2b0a0aee3d0252ef8923a043b2aa2773499))
- fix(zoom) desktop chrome clearance under text zoom ([#1490](https://github.com/block/buzz/pull/1490)) ([`ee4c9ef13`](https://github.com/block/buzz/commit/ee4c9ef139724a3cb82841d449674e6c96932b7b))
- fix(activity panel): handle back navigation ([#1487](https://github.com/block/buzz/pull/1487)) ([`caf644eda`](https://github.com/block/buzz/commit/caf644eda730c8472183e6801d428f3fd8c05ef7))

View file

@ -31,7 +31,7 @@ members = [
"crates/meridian-dev-mcp",
"examples/countdown-bot",
]
exclude = ["desktop/src-tauri"]
exclude = ["REMAPPING/meridian-desktop/src-tauri"]
resolver = "2"
[workspace.package]

View file

@ -900,8 +900,8 @@ is a measurement, not architectural enthusiasm.
| **Portable deployment** | Compose and Helm exist; the portable profile contract, Dokploy adapter, external S3/R2 qualification, Swarm profile, and Cloudflare edge modes are **scoped but not implemented**. | `meridian-dlh.1`: freeze the profile matrix and parity gate before adding a provider adapter. | Honest evaluation, single-VPS, and HA profiles using the unchanged published relay image, each with exact recovery and failure evidence. | [portable profiles epic](.settings/epics/epic-portable-deployment-profiles.md); `meridian-dlh` |
| **Preview system** | Stage metadata, stage UI, shared resolution, side-effect registry, feature-contract CI, and desktop panel E2E have landed. Five features remain in the manifest; mobile has no shared flag reader. | Add the Flutter reader/gates for Pulse and Forum; then close each feature's open high gaps. | Graduation is deletion from `preview-features.json`, backed by protocol, platform, e2e, failure-path, agent, and docs proof. | [preview graduation](.settings/features/feature-preview-graduation.md); `preview-features.json` |
| **Workflows** | **Candidate**, closest to graduation. Runtime, CLI, approvals, secrets, and e2e exist; execution history kinds still have no producer. | Dry-run before save (`W2`) and emit the 46001–46012 run/history events (`W8`); then definition versioning and suspended-run bounds. | A recoverable, observable automation surface that leaves preview without hidden execution state. | [workflow spec](.settings/features/feature-workflows.md); `meridian-ezr` |
| **Projects / NIP-34** | **Beta** with broad protocol/desktop coverage and major QA/security fixes delivered. A new P0 says the desktop cannot fetch a repo the relay actually hosts when its announcement advertises an external clone URL. The QA epic is closed while that child remains open. | Fix `meridian-ssx.6`, reopen/reconcile the parent task state, then split near-ceiling files and add repo-browser/failure e2e. | A graduated project/forge surface with one documented desktop/web Git contract and reliable external/relay-hosted interoperability. | [Projects spec](.settings/features/feature-projects.md); `meridian-ssx.6`; `meridian-hmd` |
| **Pulse / Forum / agent profiles** | Pulse and Forum are **beta** on desktop/mobile but mobile is ungated; Pulse still polls and vocabulary drifts; Forum lacks mobile voting and abuse semantics. Agent-managed profiles are **alpha** with no open high gap, but the outcome and off-semantics remain unclear. | Close mobile gating first, then Pulse realtime/parity and Forum vote/abuse gaps. Keep agent-profile scope expansion behind a real user need. | Truthful cross-platform previews with explicit failure/abuse semantics; agent autonomy that is visible and reversible. | [Pulse](.settings/features/feature-pulse.md); [Forum](.settings/features/feature-forum-channels.md); [profiles](.settings/features/feature-agent-managed-profiles.md) |
| **Projects / NIP-34** | **Beta** with broad protocol/desktop coverage and major QA/security fixes delivered. A new P0 says the desktop cannot fetch a repo the relay actually hosts when its announcement advertises an external clone URL. The QA epic is closed while that child remains open. | Fix `meridian-ssx.6`, reopen/reconcile the parent task state, then split near-ceiling files and add repo-browser/failure e2e. | A graduated project/forge surface with one documented REMAPPING/meridian-desktop/web Git contract and reliable external/relay-hosted interoperability. | [Projects spec](.settings/features/feature-projects.md); `meridian-ssx.6`; `meridian-hmd` |
| **Pulse / Forum / agent profiles** | Pulse and Forum are **beta** on REMAPPING/meridian-desktop/mobile but mobile is ungated; Pulse still polls and vocabulary drifts; Forum lacks mobile voting and abuse semantics. Agent-managed profiles are **alpha** with no open high gap, but the outcome and off-semantics remain unclear. | Close mobile gating first, then Pulse realtime/parity and Forum vote/abuse gaps. Keep agent-profile scope expansion behind a real user need. | Truthful cross-platform previews with explicit failure/abuse semantics; agent autonomy that is visible and reversible. | [Pulse](.settings/features/feature-pulse.md); [Forum](.settings/features/feature-forum-channels.md); [profiles](.settings/features/feature-agent-managed-profiles.md) |
| **Security and collaboration parity** | Granular permissions and user blocking are documented P1 gaps; kinds 39003 and 10000 exist without end-to-end readers. Link unfurl, nicknames, masquerade, slowmode, keybinds, and agent distribution remain later slices. | Implement enumerable capabilities first, then self-serve blocking; both unlock or de-risk downstream parity work. | One dated, enumerable grant model at every enforcement point and a user-visible remedy against abusive members. | [parity index](.settings/features/feature-parity-index.md); `meridian-parity.1`; `meridian-parity.2` |
| **Client surface** | Settings search, shortcut search, audio input selection, and Identity/Git readiness are delivered. Plan/usage, member-visible tool grants, top-level MCP connections, extension distribution, and shared preview artifacts remain. | Make agent tool authorization enumerable before adding marketplace/distribution polish. | A coherent settings/control surface where identity, tools, limits, integrations, and previews are visible at the scope that owns them. | [client roadmap](.settings/features/feature-client-surface-roadmap.md) |
| **Transport and performance** | Redis/Dragonfly is the launch transport. Fan-out and signature cost are measured; same-topology Redis-vs-Zenoh, opaque-frame, last-hop QoS, placement, and serialization work remain open. **Zenoh is a committed direction** as of 2026-08-06 — `zenohd` routers are the bus end-state — but it is still deferred *out of the launch*, with no dependency in tree. | Qualify Redis HA/capacity for launch; separately complete Phase 0 measurements before authorizing a transport cutover. Phase 0 now sizes the move and licenses the public claim rather than deciding whether to proceed. | QoS-classed, interest-routed transport, sequenced by measured evidence against the current path; no non-Redis launch claim by implication, and no bus-layer figure quoted as a relay number. | [Zenoh spec](.settings/features/feature-zenoh-transport.md); `meridian-ctg`; `meridian-vt1` |

View file

@ -111,19 +111,19 @@ ENV COREPACK_INTEGRITY_KEYS=${NPM_REGISTRY:+0}
RUN corepack enable
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY patches/ patches/
COPY web/package.json web/
COPY admin-web/package.json admin-web/
COPY REMAPPING/meridian-web/package.json REMAPPING/meridian-web/
COPY REMAPPING/meridian-admin-web/package.json REMAPPING/meridian-admin-web/
RUN pnpm install --frozen-lockfile --filter meridian-web --filter meridian-admin-web
COPY web/ web/
COPY admin-web/ admin-web/
# web/ and admin-web/ map `@brand/*` onto `../desktop/src/shared/ui/*` (see
# web/vite.config.ts and web/tsconfig.json), so this one shared module is a
COPY REMAPPING/meridian-web/ REMAPPING/meridian-web/
COPY REMAPPING/meridian-admin-web/ REMAPPING/meridian-admin-web/
# REMAPPING/meridian-web/ and REMAPPING/meridian-admin-web/ map `@brand/*` onto `../meridian-desktop/src/shared/ui/*` (see
# REMAPPING/meridian-web/vite.config.ts and REMAPPING/meridian-web/tsconfig.json), so this one shared module is a
# build input for the relay image even though the desktop app is not. Without
# it `tsc` fails with TS2307 on `@brand/meridian-globe/mountGlobe`. The module
# imports only itself and react, so this is its whole dependency closure.
# `.dockerignore` re-includes the same path; both are required.
COPY desktop/src/shared/ui/meridian-globe/ desktop/src/shared/ui/meridian-globe/
RUN pnpm -C web build && pnpm -C admin-web build
COPY REMAPPING/meridian-desktop/src/shared/ui/meridian-globe/ REMAPPING/meridian-desktop/src/shared/ui/meridian-globe/
RUN pnpm -C REMAPPING/meridian-web build && pnpm -C REMAPPING/meridian-admin-web build
# ─── Stage 5: shared runtime ────────────────────────────────────────────────
FROM debian:${DEBIAN_VERSION}-slim AS runtime-base
@ -149,8 +149,8 @@ RUN apt-get update \
&& useradd --system --uid 1000 --gid 1000 --home-dir /var/lib/meridian \
--create-home --shell /usr/sbin/nologin meridian
COPY --from=web-builder /build/web/dist /srv/meridian/web
COPY --from=web-builder /build/admin-web/dist /srv/meridian/admin-web
COPY --from=web-builder /build/REMAPPING/meridian-web/dist /srv/meridian/web
COPY --from=web-builder /build/REMAPPING/meridian-admin-web/dist /srv/meridian/admin-web
# The invite landing page is always served from the bundled web UI. Repository
# browser routes require the separate MERIDIAN_SERVE_GIT_WEB_GUI=true opt-in. The

View file

@ -10,9 +10,9 @@ set dotenv-load := true
# those read the environment rather than going through Compose.
export COMPOSE_ENV_FILES := if path_exists(".env.local") == "true" { ".env,.env.local" } else { ".env" }
desktop_dir := "desktop"
desktop_tauri_manifest := "desktop/src-tauri/Cargo.toml"
web_dir := "web"
desktop_dir := "REMAPPING/meridian-desktop"
desktop_tauri_manifest := "REMAPPING/meridian-desktop/src-tauri/Cargo.toml"
web_dir := "REMAPPING/meridian-web"
# Opt-in mesh-llm. Off by default so `just dev`/`just staging`/`just production`
# skip ~420 extra crates + the llama.cpp native runtime build and stay fast to
@ -103,7 +103,7 @@ dev-secrets-to-files *ARGS:
./scripts/dev-secrets-to-files.sh {{ARGS}}
# Unattended equivalent of the Keychain Access → Certificate Assistant steps in
# desktop/src-tauri/AGENTS.md. Needed for CI and any machine nobody can click
# REMAPPING/meridian-desktop/src-tauri/AGENTS.md. Needed for CI and any machine nobody can click
# through. Idempotent; set MERIDIAN_DEV_SIGN_IDENTITY afterwards to use it.
# macOS: create the self-signed identity that keeps dev builds' code identity stable
@ -465,9 +465,9 @@ _ensure-sidecar-stubs:
#!/usr/bin/env bash
set -euo pipefail
TARGET=$(rustc -vV | sed -n 's|host: ||p')
mkdir -p desktop/src-tauri/binaries
mkdir -p REMAPPING/meridian-desktop/src-tauri/binaries
for bin in meridian-acp meridian-agent meridian-dev-mcp git-credential-nostr meridian; do
touch "desktop/src-tauri/binaries/${bin}-${TARGET}"
touch "REMAPPING/meridian-desktop/src-tauri/binaries/${bin}-${TARGET}"
done
# Ensure Docker dev services (Postgres, Dragonfly, etc.) are running and healthy
@ -523,7 +523,7 @@ desktop-tauri-check: _ensure-sidecar-stubs
# Run desktop Tauri Rust unit tests
desktop-tauri-test: _ensure-sidecar-stubs
cd desktop/src-tauri && cargo test
cd REMAPPING/meridian-desktop/src-tauri && cargo test
# Verify compiled-flag behavior under both compile states (clean + internal).
# Runs the observer_archive focused test twice with independently supplied
@ -531,7 +531,7 @@ desktop-tauri-test: _ensure-sidecar-stubs
desktop-tauri-test-compiled-flags: _ensure-sidecar-stubs
#!/usr/bin/env bash
set -euo pipefail
cd desktop/src-tauri
cd REMAPPING/meridian-desktop/src-tauri
echo "=== Clean build (no flag) → expect false ==="
env -u MERIDIAN_BUILD_OBSERVER_ARCHIVE_DEFAULT \
-u MERIDIAN_BUILD_AUTO_CONNECT_DEFAULT_RELAY \
@ -1256,8 +1256,8 @@ relay-web: bootstrap _ensure-migrations
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
[[ -d node_modules ]] || pnpm install
pnpm -C web build
MERIDIAN_WEB_DIR=./web/dist cargo run -p meridian-relay
pnpm -C REMAPPING/meridian-web build
MERIDIAN_WEB_DIR=./REMAPPING/meridian-web/dist cargo run -p meridian-relay
# Build and run the private read-only admin dashboard
admin: bootstrap _ensure-migrations
@ -1265,9 +1265,9 @@ admin: bootstrap _ensure-migrations
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
[[ -d node_modules ]] || pnpm install
pnpm -C admin-web build
pnpm -C REMAPPING/meridian-admin-web build
export MERIDIAN_ADMIN_HOST="${MERIDIAN_ADMIN_HOST:-admin.localhost:3000}"
export MERIDIAN_ADMIN_WEB_DIR="${MERIDIAN_ADMIN_WEB_DIR:-{{justfile_directory()}}/admin-web/dist}"
export MERIDIAN_ADMIN_WEB_DIR="${MERIDIAN_ADMIN_WEB_DIR:-{{justfile_directory()}}/REMAPPING/meridian-admin-web/dist}"
echo "Admin dashboard: http://${MERIDIAN_ADMIN_HOST}/reports"
cargo run -p meridian-relay
@ -1277,20 +1277,20 @@ admin-seed: _ensure-migrations
# Build admin-web and run its browser suite — the operator console's only tests.
# The build is not optional and must not be split from the run: the Playwright
# config serves `admin-web/dist` through `vite preview`, and `dist/` is
# config serves `REMAPPING/meridian-admin-web/dist` through `vite preview`, and `dist/` is
# gitignored, so on a clean checkout a bare `playwright test` dies with
# "The directory \"dist\" does not exist" (F087). `pnpm test:e2e` is
# `pnpm build && playwright test`, which is why this delegates rather than
# calling playwright directly.
admin-web-e2e:
pnpm -C admin-web test:e2e
pnpm -C REMAPPING/meridian-admin-web test:e2e
# Run focused relay and browser checks for the read-only admin dashboard
admin-check: fmt-check admin-web-e2e
cargo check -p meridian-relay --all-targets
cargo test -p meridian-relay api::admin
cargo test -p meridian-relay router::tests
pnpm -C admin-web check
pnpm -C REMAPPING/meridian-admin-web check
# Start the relay server in release mode
relay-release: _ensure-migrations
@ -1357,8 +1357,8 @@ dev *ARGS: bootstrap _ensure-sidecar-stubs _ensure-migrations
echo "Starting on Vite port ${MERIDIAN_VITE_PORT}, relay ${MERIDIAN_RELAY_URL}"
FEATURES=(); [[ -n "{{mesh}}" ]] && FEATURES=(--features mesh-llm)
if [[ "$(uname -s)" == "Darwin" && -n "${MERIDIAN_DEV_SIGN_IDENTITY:-}" ]]; then
# Stable dev code signing: OS grants survive rebuilds. See desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
# Stable dev code signing: OS grants survive rebuilds. See REMAPPING/meridian-desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/REMAPPING/meridian-desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
fi
pnpm exec tauri dev ${FEATURES[@]+"${FEATURES[@]}"} --config "$MERIDIAN_TAURI_CONFIG" {{ARGS}}
@ -1372,8 +1372,8 @@ desktop-standalone *ARGS: _ensure-sidecar-stubs
TARGET=$(rustc -vV | sed -n 's|host: ||p')
TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | node -p "JSON.parse(require('fs').readFileSync(0, 'utf8')).target_directory")
for bin in meridian-acp meridian-agent meridian-dev-mcp git-credential-nostr meridian; do
cp "${TARGET_DIR}/debug/${bin}" "desktop/src-tauri/binaries/${bin}-${TARGET}"
chmod +x "desktop/src-tauri/binaries/${bin}-${TARGET}"
cp "${TARGET_DIR}/debug/${bin}" "REMAPPING/meridian-desktop/src-tauri/binaries/${bin}-${TARGET}"
chmod +x "REMAPPING/meridian-desktop/src-tauri/binaries/${bin}-${TARGET}"
done
cd {{desktop_dir}}
[[ -d node_modules ]] || pnpm install
@ -1390,8 +1390,8 @@ desktop-standalone *ARGS: _ensure-sidecar-stubs
trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID" || true' EXIT
echo "Starting standalone desktop on Vite port ${MERIDIAN_VITE_PORT}; no relay services were started"
if [[ "$(uname -s)" == "Darwin" && -n "${MERIDIAN_DEV_SIGN_IDENTITY:-}" ]]; then
# Stable dev code signing: OS grants survive rebuilds. See desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
# Stable dev code signing: OS grants survive rebuilds. See REMAPPING/meridian-desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/REMAPPING/meridian-desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
fi
pnpm exec tauri dev --config "$MERIDIAN_TAURI_CONFIG" {{ARGS}}
@ -1410,8 +1410,8 @@ staging *ARGS: bootstrap _ensure-sidecar-stubs
# Replace the 0-byte sidecar stub with the real CLI binary so tauri dev picks it up.
TARGET=$(rustc -vV | sed -n 's|host: ||p')
TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | node -p "JSON.parse(require('fs').readFileSync(0, 'utf8')).target_directory")
cp "${TARGET_DIR}/release/meridian" "desktop/src-tauri/binaries/meridian-${TARGET}"
chmod +x "desktop/src-tauri/binaries/meridian-${TARGET}"
cp "${TARGET_DIR}/release/meridian" "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-${TARGET}"
chmod +x "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-${TARGET}"
cd {{desktop_dir}}
export MERIDIAN_RELAY_URL="wss://relay.meridian.r2d2.office.ilab.zone"
source ../scripts/instance-env.sh
@ -1421,8 +1421,8 @@ staging *ARGS: bootstrap _ensure-sidecar-stubs
trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID" || true' EXIT
echo "Starting staging on Vite port ${MERIDIAN_VITE_PORT}, relay ${MERIDIAN_RELAY_URL}"
if [[ "$(uname -s)" == "Darwin" && -n "${MERIDIAN_DEV_SIGN_IDENTITY:-}" ]]; then
# Stable dev code signing: OS grants survive rebuilds. See desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
# Stable dev code signing: OS grants survive rebuilds. See REMAPPING/meridian-desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/REMAPPING/meridian-desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
fi
pnpm exec tauri dev ${FEATURES[@]+"${FEATURES[@]}"} --config "$MERIDIAN_TAURI_CONFIG" {{ARGS}}
@ -1441,8 +1441,8 @@ production *ARGS: bootstrap _ensure-sidecar-stubs
# Replace the 0-byte sidecar stub with the real CLI binary so tauri dev picks it up.
TARGET=$(rustc -vV | sed -n 's|host: ||p')
TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | node -p "JSON.parse(require('fs').readFileSync(0, 'utf8')).target_directory")
cp "${TARGET_DIR}/release/meridian" "desktop/src-tauri/binaries/meridian-${TARGET}"
chmod +x "desktop/src-tauri/binaries/meridian-${TARGET}"
cp "${TARGET_DIR}/release/meridian" "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-${TARGET}"
chmod +x "REMAPPING/meridian-desktop/src-tauri/binaries/meridian-${TARGET}"
cd {{desktop_dir}}
export MERIDIAN_RELAY_URL="wss://meridian.block.builderlab.xyz"
source ../scripts/instance-env.sh
@ -1452,8 +1452,8 @@ production *ARGS: bootstrap _ensure-sidecar-stubs
trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID" || true' EXIT
echo "Starting production on Vite port ${MERIDIAN_VITE_PORT}, relay ${MERIDIAN_RELAY_URL}"
if [[ "$(uname -s)" == "Darwin" && -n "${MERIDIAN_DEV_SIGN_IDENTITY:-}" ]]; then
# Stable dev code signing: OS grants survive rebuilds. See desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
# Stable dev code signing: OS grants survive rebuilds. See REMAPPING/meridian-desktop/src-tauri/AGENTS.md.
export "CARGO_TARGET_$(rustc -vV | sed -n 's|host: ||p' | tr 'a-z-' 'A-Z_')_RUNNER={{justfile_directory()}}/REMAPPING/meridian-desktop/src-tauri/scripts/macos-dev-sign-runner.sh"
fi
pnpm exec tauri dev ${FEATURES[@]+"${FEATURES[@]}"} --config "$MERIDIAN_TAURI_CONFIG" {{ARGS}}
@ -1503,7 +1503,7 @@ web-e2e-smoke:
# ─── Mobile ──────────────────────────────────────────────────────────────────
mobile_dir := "mobile"
mobile_dir := "REMAPPING/meridian-mobile"
# Install mobile Flutter dependencies
mobile-install:
@ -1557,7 +1557,7 @@ migrate: _ensure-migrations
# Remove build artifacts
clean:
cargo clean
cargo clean --manifest-path desktop/src-tauri/Cargo.toml
cargo clean --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml
# Reclaim incremental-cache disk in both target trees without a cold rebuild
target-sweep *ARGS:
@ -1571,7 +1571,7 @@ check-compile:
# Read the current desktop version from package.json
get-current-version:
@node -p "require('./desktop/package.json').version"
@node -p "require('./REMAPPING/meridian-desktop/package.json').version"
# Read the current relay version from its crate manifest
get-current-relay-version:
@ -1593,29 +1593,29 @@ get-next-relay-patch-version:
bump-desktop-version version:
#!/usr/bin/env bash
set -euo pipefail
# desktop/package.json
cd desktop && npm pkg set "version={{ version }}" && cd ..
# desktop/src-tauri/tauri.conf.json
# REMAPPING/meridian-desktop/package.json
cd REMAPPING/meridian-desktop && npm pkg set "version={{ version }}" && cd ..
# REMAPPING/meridian-desktop/src-tauri/tauri.conf.json
node -e "
const fs = require('fs');
const p = 'desktop/src-tauri/tauri.conf.json';
const p = 'REMAPPING/meridian-desktop/src-tauri/tauri.conf.json';
const c = JSON.parse(fs.readFileSync(p, 'utf8'));
c.version = '{{ version }}';
fs.writeFileSync(p, JSON.stringify(c, null, 2) + '\n');
"
# JSON.stringify expands arrays/objects in a way biome rejects; reformat to match.
(cd desktop && pnpm exec biome format --write src-tauri/tauri.conf.json)
# desktop/src-tauri/Cargo.toml — only first version line (under [package])
(cd REMAPPING/meridian-desktop && pnpm exec biome format --write src-tauri/tauri.conf.json)
# REMAPPING/meridian-desktop/src-tauri/Cargo.toml — only first version line (under [package])
node -e "
const fs = require('fs');
const p = 'desktop/src-tauri/Cargo.toml';
const p = 'REMAPPING/meridian-desktop/src-tauri/Cargo.toml';
let t = fs.readFileSync(p, 'utf8');
t = t.replace(/^version = \".*\"/m, 'version = \"{{ version }}\"');
fs.writeFileSync(p, t);
"
# Regenerate lockfiles
pnpm install --lockfile-only
cargo update -p meridian-desktop --manifest-path desktop/src-tauri/Cargo.toml
cargo update -p meridian-desktop --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml
echo "Bumped desktop manifests to {{ version }} and regenerated lockfiles"
# Bump the relay crate version and regenerate the lockfile
@ -1671,8 +1671,8 @@ _release-pr lane version:
TAG_EXCLUDE='*-*'
TAG_PREFIX="v"
CHANGELOG="CHANGELOG.md"
ADD_FILES=(desktop/package.json desktop/src-tauri/tauri.conf.json desktop/src-tauri/Cargo.toml desktop/src-tauri/Cargo.lock pnpm-lock.yaml CHANGELOG.md)
LOG_PATHS=(desktop/ crates/meridian-core/ crates/meridian-persona/ crates/meridian-sdk/ crates/meridian-agent/)
ADD_FILES=(REMAPPING/meridian-desktop/package.json REMAPPING/meridian-desktop/src-tauri/tauri.conf.json REMAPPING/meridian-desktop/src-tauri/Cargo.toml REMAPPING/meridian-desktop/src-tauri/Cargo.lock pnpm-lock.yaml CHANGELOG.md)
LOG_PATHS=(REMAPPING/meridian-desktop/ crates/meridian-core/ crates/meridian-persona/ crates/meridian-sdk/ crates/meridian-agent/)
ARTIFACT="Meridian Desktop" ;;
relay)
BRANCH_PREFIX="relay-release"

View file

@ -628,10 +628,10 @@ vendored copy of something released elsewhere.
R2D2-MERIDIAN/
│
├── crates/ Rust workspace — the relay and every binary shipped beside it
├── desktop/ Tauri 2 + React 19 — the primary human client
├── web/ Browser client served by the relay: repo browser + invite accept
├── admin-web/ Read-only operator dashboard, served on the admin host
├── mobile/ Flutter (iOS + Android) — Riverpod + hooks
├── REMAPPING/meridian-desktop/ Tauri 2 + React 19 — the primary human client
├── REMAPPING/meridian-web/ Browser client served by the relay: repo browser + invite accept
├── REMAPPING/meridian-admin-web/ Read-only operator dashboard, served on the admin host
├── REMAPPING/meridian-mobile/ Flutter (iOS + Android) — Riverpod + hooks
│
├── migrations/ Forward-only Postgres schema, applied on relay startup
├── schema/ schema.sql — the flattened current schema, for reading only
@ -666,10 +666,10 @@ Seven deployable things come out of this repo. Each one has exactly one home.
| Component | Built with | Lives in | Run it |
| --- | --- | --- | --- |
| **Relay** — the server, and the only enforcement point | Rust · Axum · Tokio | `crates/meridian-relay` | `just relay` → `ws://localhost:3000` |
| **Desktop** — the primary human client | Tauri 2 · React 19 · Vite · Tailwind | `desktop/` | `just dev` (native shell) · `just desktop-dev` (browser) |
| **Web** — the narrow browser surface | React 19 · Vite | `web/` | built to `web/dist`, served by the relay |
| **Operator dashboard** — read-only ops view | React 19 · Vite · plain CSS | `admin-web/` | `just admin` → served on the admin host |
| **Mobile** | Flutter · Riverpod · hooks | `mobile/` | `just mobile-dev` |
| **Desktop** — the primary human client | Tauri 2 · React 19 · Vite · Tailwind | `REMAPPING/meridian-desktop/` | `just dev` (native shell) · `just desktop-dev` (browser) |
| **Web** — the narrow browser surface | React 19 · Vite | `REMAPPING/meridian-web/` | built to `REMAPPING/meridian-web/dist`, served by the relay |
| **Operator dashboard** — read-only ops view | React 19 · Vite · plain CSS | `REMAPPING/meridian-admin-web/` | `just admin` → served on the admin host |
| **Mobile** | Flutter · Riverpod · hooks | `REMAPPING/meridian-mobile/` | `just mobile-dev` |
| **Control plane** — relay provisioning | Rust · Axum | `crates/meridian-control-plane` | `./run.sh control-plane` |
| **Push gateway** — APNs, blind by design | Rust · Axum | `crates/meridian-push-gateway` | own image, own deployment |
@ -684,7 +684,7 @@ the push gateway is deliberately blind to message content.
#### The relay — `crates/meridian-relay`
One Axum process. It terminates the WebSocket protocol, answers the narrow HTTP
surface, hosts git and huddle audio, and serves the `web/` and `admin-web/`
surface, hosts git and huddle audio, and serves the `REMAPPING/meridian-web/` and `REMAPPING/meridian-admin-web/`
bundles. **Every access decision in the system is made here** — no client, no
bridge and no downstream consumer may make one.
@ -713,14 +713,14 @@ The relay orchestrates every subsystem crate by **direct call**, and no subsyste
reaches sideways for a capability the relay owns. That is why one process can be
reasoned about — see [System shape](#system-shape).
#### Desktop — `desktop/`
#### Desktop — `REMAPPING/meridian-desktop/`
A Tauri 2 app: a Rust native shell around a React 19 webview. The split matters —
anything touching the OS keychain, the filesystem, a native socket or a
subprocess lives in Rust; everything else is React.
```
desktop/
REMAPPING/meridian-desktop/
├── src/ the React 19 front end
│ ├── app/ shell, router, community remount boundary, shortcuts
│ ├── features/ one folder per product area (channels, chat, agents,
@ -740,16 +740,16 @@ desktop/
└── tests/e2e/ Playwright specs (mock bridge + live relay)
```
Two traps this layout sets, both documented in [`desktop/AGENTS.md`](desktop/AGENTS.md):
Two traps this layout sets, both documented in [`REMAPPING/meridian-desktop/AGENTS.md`](REMAPPING/meridian-desktop/AGENTS.md):
- **`desktop/src-tauri` is excluded from the root Cargo workspace.** `cargo test`
- **`REMAPPING/meridian-desktop/src-tauri` is excluded from the root Cargo workspace.** `cargo test`
at the repo root does not run it. Use
`cargo test --manifest-path desktop/src-tauri/Cargo.toml`.
`cargo test --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml`.
- **Switching community remounts React but does not reload the page.** Any
module-level cache you add must be reset in `resetCommunityState()`, or data
from the previous community leaks into the next one.
#### Web — `web/`
#### Web — `REMAPPING/meridian-web/`
Deliberately narrow, and kept that way. It is **not** a browser port of the
desktop app; it exists so that a link works without an install:
@ -760,7 +760,7 @@ desktop app; it exists so that a link works without an install:
The relay serves the built bundle. Adding a third feature here is a decision, not
a routine change — the desktop app is where client surface belongs.
#### Operator dashboard — `admin-web/`
#### Operator dashboard — `REMAPPING/meridian-admin-web/`
About ten files, no state library, no design system, no Tailwind. Its value is
that an operator can read the whole thing in one sitting, so it stays
@ -773,14 +773,14 @@ pipeline. `src/types.ts` mirrors the relay's admin response contract in
`tests/routes.spec.ts` fails on drift. A metric the relay does not report renders
as "Not reported", never as zero: an absent metric is not evidence of health.
#### Mobile — `mobile/`
#### Mobile — `REMAPPING/meridian-mobile/`
Flutter, Riverpod + `flutter_hooks`, Catppuccin theming matched to desktop.
Features under `lib/features/` (activity, channels, forum, home, invites,
pairing, profile, pulse, search, settings), shared code in `lib/shared/`.
Event kinds in `lib/shared/relay/nostr_models.dart` must stay in sync with
`desktop/src/shared/constants/kinds.ts`. Agents may run `flutter test`,
`REMAPPING/meridian-desktop/src/shared/constants/kinds.ts`. Agents may run `flutter test`,
`flutter analyze` and `dart format` — **never** `flutter run`, `build`, `clean`
or `upgrade`.
@ -887,9 +887,9 @@ weaken a parent's constraint.
| Contract | Governs |
| --- | --- |
| [`crates/AGENTS.md`](crates/AGENTS.md) | The Rust workspace, with nested contracts for relay, core, db, acp, agent, cli, test-client |
| [`desktop/AGENTS.md`](desktop/AGENTS.md) | The desktop app; nested under `src/features/`, `src-tauri/`, `tests/` |
| [`mobile/AGENTS.md`](mobile/AGENTS.md) | Flutter rules, the file-size ceiling, which `flutter` commands are safe |
| [`web/AGENTS.md`](web/AGENTS.md) · [`admin-web/AGENTS.md`](admin-web/AGENTS.md) | The two browser surfaces and the relay admin API contract |
| [`REMAPPING/meridian-desktop/AGENTS.md`](REMAPPING/meridian-desktop/AGENTS.md) | The desktop app; nested under `src/features/`, `src-tauri/`, `tests/` |
| [`REMAPPING/meridian-mobile/AGENTS.md`](REMAPPING/meridian-mobile/AGENTS.md) | Flutter rules, the file-size ceiling, which `flutter` commands are safe |
| [`REMAPPING/meridian-web/AGENTS.md`](REMAPPING/meridian-web/AGENTS.md) · [`REMAPPING/meridian-admin-web/AGENTS.md`](REMAPPING/meridian-admin-web/AGENTS.md) | The two browser surfaces and the relay admin API contract |
| [`migrations/AGENTS.md`](migrations/AGENTS.md) | Forward-only schema, checksum-frozen files, partition and fence invariants |
| [`docs/AGENTS.md`](docs/AGENTS.md) | NIPs, MIPs, TLA+/Tamarin models, operator guides |
| [`deploy/AGENTS.md`](deploy/AGENTS.md) | Helm charts and the Compose stack; values, schema and test contract |

View file

@ -115,9 +115,9 @@ that reach `main` become part of every later candidate, and there is no retained
hotfix branch or branch-ancestry history. Add a dedicated hotfix flow later if a
release actually needs isolation from `main`.
`mobile/pubspec.yaml` keeps `0.0.0+1` only as a valid, visibly non-release
`REMAPPING/meridian-mobile/pubspec.yaml` keeps `0.0.0+1` only as a valid, visibly non-release
fallback for local development and validation builds. Release jobs always
inject both version fields. `mobile/CHANGELOG.md` is retained as historical
inject both version fields. `REMAPPING/meridian-mobile/CHANGELOG.md` is retained as historical
release data. It is not a release ledger for this flow.
---
@ -126,7 +126,7 @@ release data. It is not a release ledger for this flow.
| Lane | Release version authority |
|------|---------------------------|
| Desktop | `desktop/package.json` and synchronized desktop manifests |
| Desktop | `REMAPPING/meridian-desktop/package.json` and synchronized desktop manifests |
| Relay | `crates/meridian-relay/Cargo.toml` |
| Mobile | Exact `mobile-vX.Y.Z-rc.N` remote tag |
@ -211,7 +211,7 @@ Silicon (`darwin-aarch64`, the `release` job) and Intel
`.AppImage`. Both macOS DMGs are codesigned, notarized, and attached to
the same `v<version>` release. Intel users download the `_x64.dmg`.
The Linux AppImage is post-processed by `desktop/scripts/fix-appimage.sh`,
The Linux AppImage is post-processed by `REMAPPING/meridian-desktop/scripts/fix-appimage.sh`,
which strips infra libraries over-bundled by linuxdeploy (they crash on
Mesa 25+ / GLib 2.88 distros; see
[tauri-apps/tauri#15665](https://github.com/tauri-apps/tauri/issues/15665))

117
REMAPPING/.gitignore vendored Normal file
View file

@ -0,0 +1,117 @@
# Created by https://www.toptal.com/developers/gitignore/api/phpstorm
# Edit at https://www.toptal.com/developers/gitignore?templates=phpstorm
### PhpStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using auto-import.
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
.idea/encodings.xml
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### PhpStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
# End of https://www.toptal.com/developers/gitignore/api/phpstorm
#Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

1
REMAPPING/.gitlab-ci.yml Normal file
View file

@ -0,0 +1 @@
include: 'cicd/pipeline.yml'

View file

@ -0,0 +1,16 @@
## Problem description:
- Necessary steps to reproduce the issue and/or
- Observable erroneous effects
## Expected behaviour:
- Decription of what is expected instead of erroneous behaviour
## Exit criteria:
- [ ] Subtask or Deliverable
- [ ] Subtask or Deliverable
## Additional comments
## GitLab quick actions - do not change below
/label ~"Status::To do"

View file

@ -0,0 +1,22 @@
## Rationale:
_Provide clear rationale of the task. Indicate why it is important. Suggest an epic._
## Description of the task:
_Describe what should be done in general. What is expected output and the value._
## Inputs/dependencies:
- Description of necessary inputs required to perform the task
- Description of necessary inputs required to perform the task
## Main efforts:
- Description of what to be done
- Description of what to be done
## Deliverables/exit criteria/test cases:
- [ ] Subtask or Deliverable
- [ ] Subtask or Deliverable
## Additional comments
## GitLab quick actions - do not change below
/label ~"Status::To do"

View file

@ -0,0 +1,11 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: detect-aws-credentials
- id: detect-private-key
- repo: https://github.com/Yelp/detect-secrets
rev: v1.3.0
hooks:
- id: detect-secrets

392
REMAPPING/AGENTS.md Normal file
View file

@ -0,0 +1,392 @@
# AGENTS.md — REMAPPING
## Purpose
`REMAPPING/` is the **IHLC delivery grammar** inside this checkout: one top-level
directory per deployable. Infrastructure directories hold Dockerfiles and config
only; client directories (`meridian-desktop/`, `meridian-web/`, `meridian-mobile/`,
`meridian-admin-web/`) hold the application source that used to live at the
repo root.
**Provenance.** This arrived as a clone of the IHLC GitLab project. Its nested
`.git` has since been removed, so none of the following is verifiable from the
tree any more — it is recorded here because this is now the only place it
exists:
| Fact | Value |
| --- | --- |
| Origin | `https://gitlab.ilab.zone/ihlc/teams/mvp-teams/ih-mvp-teams/team-alpha/meridian/meridian.git` |
| Cloned at | branch `dev`, `39be097` "Cleanup" (2026-08-07) |
| History | 4 commits — `2703505` template project commit (2022-04-05), `4c98bbe` "Initialize from template: MERIDIAN", `89ebc46`, `39be097` |
| Branches | `dev`, `master`, `staging` — all three at `39be097`, nothing diverged |
Pushing this content back to that GitLab project is therefore a fresh push, not
a fast-forward: the shared history is gone. Re-clone alongside and diff before
assuming any file here matches what IHLC has.
It began as the **IHLC delivery scaffold** for a project named `meridian`: a
GitLab CI/CD pipeline, an IronBank-sourced Keycloak + PostgreSQL + OpenResty
stack, a Keycloak realm export, and GitLab issue templates, with every
application-level file left as template prompts.
It is now the **delivery repository for MERIDIAN in the IHLC environments**, and
the home of the client trees that share its directory grammar. Infrastructure
deployables hold per-environment Dockerfiles and config, never their upstream
source: `meridian-keycloak/` holds Keycloak's config and not Keycloak's source;
`meridian-relay/` re-tags the published relay image and does not compile Rust.
Client deployables are the opposite — they *are* the source, and the parent's
`just` / pnpm / CI recipes build them in place.
Two properties are load-bearing:
- **Infrastructure directories never hold application source.** The relay image
is built by the parent tree and published to
`registry.r2d2.office.ilab.zone/meridian`; the Dockerfiles here re-tag it and
bake its runtime shape.
- **Client directories here are first-class members of the parent build.** The
parent's Justfile, pnpm workspace, lefthook, and CI point at
`REMAPPING/meridian-*` paths; do not treat this tree as an opaque vendored
clone.
Residue from the template that has *not* been resolved: `LICENSE` reads
`TO BE PROVIDED`, `.gitignore` is the toptal PhpStorm template, and the realm is
still named `dragon-egg-green` rather than `meridian`.
## Ownership
- **In the STELLAR chain.** With the nested `.git` removed, `scripts/stellar-scan.mjs`
sees every path here, so this doc is a normal child of the root `AGENTS.md`
and is listed in its Child STELLAR Index. Removing that row makes this an
`orphan-doc` error in `just check-stellar`; adding a nested `AGENTS.md` under
any subdirectory here means indexing it below.
- **This doc owns every path under `REMAPPING/`** that no nearer doc claims —
the six deployable directories, `cicd/`, `.gitlab/`, and the root files.
- **Two audiences, one tree.** The content is delivery configuration for the
IHLC GitLab project, but it now lives in this repo and commits land on
`git.office.ilab.zone`. Anything pushed to `gitlab.ilab.zone` is a publish to
a third party — ask before doing it, and diff against a fresh clone first
(see Provenance).
## Local Contracts
### Layout
| Path | Contract |
| --- | --- |
| `.gitlab-ci.yml` | One line: `include: 'cicd/pipeline.yml'`. All pipeline logic lives under `cicd/` |
| `cicd/pipeline.yml` | Entry point — stages, workflow rules, global variables, external template includes |
| `cicd/pipeline-common.yml` | Reusable hidden jobs (`.compile-backend-base`, `.compile-dragonegg-frontend-base`, `.build-image-base`) plus the `remove-images` cleanup job |
| `cicd/pipeline-dev.yml` / `-stg.yml` | Per-environment build + deploy + scan jobs, extending the common bases |
| `cicd/pipeline-feature-branch.yml` | Merge-request SonarQube job; the Maven compile job is commented out |
| `cicd/docker-compose.yml` | The deployment. Used unchanged by local, dev and staging |
| `cicd/docker-compose-build.yml` | Build overlay, **local only** — CI builds with `docker build`, not Compose |
| `cicd/.env.local` / `.env.dev` / `.env.staging` | One env file per `TAG`; read by both `docker build` and `docker compose` |
| `meridian-relay/` | The application. `Dockerfile.*` re-tag `registry.r2d2.office.ilab.zone/meridian` and bake listeners, git path and `MERIDIAN_AUTO_MIGRATE`. No Rust is compiled here |
| `meridian-postgresql/` | The **relay's** Postgres 17 — system of record for events. Deliberately not the Keycloak database |
| `meridian-dragonfly/` | Dragonfly v1.39.0 — pub/sub fan-out, presence, rate limits over the Redis wire protocol |
| `meridian-keycloak/` | Keycloak 22.0.1 image + `configuration/realms/*.json` realm export |
| `meridian-keycloak-postgresql/` | Postgres 15.4-alpine3.18 image — a `FROM` line and nothing else |
| `meridian-proxy/` | OpenResty 1.21.4.2 reverse proxy + `nginx/*.conf`. The only service published to the host |
| `meridian-desktop/` | Tauri desktop client. **No Dockerfile, no Compose service** — ships an installable artifact; `compile`-stage territory. See its README |
| `meridian-mobile/` | Flutter mobile client. Same: artifact, not container. See its README |
**The directory grammar is the contract.** One top-level directory per
deployable, holding that component's per-environment Dockerfiles and config and
never its source. A component that ships an installable artifact instead of a
container still gets a directory — it just has no `Dockerfile.*` and no service
in `cicd/docker-compose.yml`, and says so in its README rather than by absence.
| `.gitlab/issue_templates/` | `Bug.md`, `Task.md` — both end in `/label ~"Status::To do"` |
| `sonar-project.properties` | `projectName=meridian`, `projectVersion=1.0.0`, `sources=.`; no `sonar.projectKey` (the pipeline supplies it) |
| `.pre-commit-config.yaml` | `detect-aws-credentials`, `detect-private-key` (pre-commit-hooks v4.3.0), `detect-secrets` (v1.3.0) |
### Pipeline contract
- Stages: `compile sast build deploy apitest dasttest cleanup`. **`compile` and
`apitest` have no active jobs** — the only compile job is commented out and
nothing declares `stage: apitest`. They are placeholders for a backend this
scaffold never received.
- Pipelines run only on `push` and `web` sources (`workflow.rules`).
- Branch → environment is hard-wired, one branch per environment:
| Branch | Builds | Deploys to | URL | Scans |
| --- | --- | --- | --- | --- |
| `dev` | `Dockerfile.dev`, `.env.dev` | `dev-docker-01` as `developer` | `https://meridian-dev.ilab.zone/` | SonarQube |
| `staging` | `Dockerfile.staging`, `.env.staging` | `stg-docker-01` as `developer` | `https://meridian-stg.ilab.zone/` | SonarQube + ZAP baseline |
| merge request | — | — | — | SonarQube (feature-branch template) |
| `master` | — | — | — | only `remove-images` cleanup |
- Deploy is **scp + `docker compose`, not an orchestrator**: `mkdir -p
/home/developer/meridian`, copy `cicd/docker-compose.yml` and the env file to
`.env`, append `IMAGE_PATH=$JF_DOCKER_PROJECT_REGISTRY/`, `docker compose down
--remove-orphans`, delete every local project image, `docker compose up -d`.
SSH keys come from `$MERIDIAN_DEV_DOCKER_PEM` / `$MERIDIAN_STG_DOCKER_PEM`
(base64), with `StrictHostKeyChecking=no` on every hop.
- **`RESET_VOLUMES=1` adds `--volumes` to that `down`, destroying the Keycloak
database** — every user, session and realm change made through the admin
console. It is a CI/CD variable, so it is one checkbox away at all times.
- **Image tags are environment names, not versions.** Images push to
`$JF_DOCKER_PROJECT_REGISTRY/<service-folder>:$TAG` where `TAG` ∈
`{local,dev,staging}`, so a deploy is "whatever `:dev` means right now" and
carries no provenance. This is the failure the parent's Design Law names —
*"a deployed image predating the enforcement code makes a feature flag a
silent no-op"* — and it cannot be diagnosed from the tag here.
- External includes are **unpinned**: ZAP and SonarQube templates come from
`ihlc/teams/team-nightwatch/servicestream/configuration-templates` at
`ref: master`. An upstream edit changes this pipeline with no commit here.
- `SONARQUBE_PROJECT_KEY: team-alpha-meridian` must follow `<team>-<project>`
and must match `sonar-project.properties` **if** that file declares a key. It
currently does not, so the pipeline value is authoritative — keep it that way
or set both.
- Base images come from IronBank only: `ironbank.ilab.zone` for `local`,
`ironbank.ilab.private:8082` for `dev`/`staging`. Nothing pulls Docker Hub.
Off the IHLC network, **a local build cannot pull its base images** — that is
the expected failure, not a broken Dockerfile.
- Jobs run on runners tagged `docker`; registry auth is `DOCKER_AUTH_CONFIG`
built from `$JF_CREDENTIALS_BASE64`, Maven from `$JF_MAVEN_SETTINGS`, npm from
`$JF_NPM_PROXY_REGISTRY` + `$JF_ACCESS_TOKEN`.
- ZAP **full scan and API scan are commented out**; only the baseline scan runs,
and only against staging. The comments say why: the full scan performs real
attacks and can damage the deployment.
### Environment and port contract
- Three env files, one per `TAG`. Their values feed **both** `docker build` and
`docker compose`, so a port or name change is never one-sided — the files say
so in their own header, and it is true.
- `local` sets `COMPOSE_FILE=docker-compose-build.yml:docker-compose.yml` and an
**empty `IMAGE_PATH`** (build locally, no registry). `dev`/`staging` list only
`docker-compose.yml`; the pipeline appends `IMAGE_PATH` at deploy time.
- **Container names carry the tag, and three other places hard-code those
names.** `container_name: meridian-<component>-${TAG}` in Compose is repeated
in (1) the nginx upstreams per environment — `meridian-relay-local:3000` and
`meridian-keycloak-local:8090` in `nginx/local.conf`, `-dev` in `dev.conf`,
`-staging` in `staging.conf`; (2) the relay's `DATABASE_URL`, which names
`meridian-postgresql-${TAG}`; (3) the relay's `REDIS_URL`, which names
`meridian-dragonfly-${TAG}`. Compose interpolates (2) and (3), so they follow
`TAG` automatically; the nginx confs do **not** and must be edited by hand.
Getting it wrong leaves the proxy resolving a host that no longer exists — a
502 with every container healthy. **This is the one cross-file coupling to
check on any rename.**
- **The relay's runtime shape is baked into its image, not set per environment.**
`MERIDIAN_BIND_ADDR`, `MERIDIAN_HEALTH_PORT`, `MERIDIAN_METRICS_PORT`,
`MERIDIAN_GIT_REPO_PATH` and `MERIDIAN_AUTO_MIGRATE` live in
`meridian-relay/Dockerfile.*` so they cannot drift between environments. Only
the two values that *must* differ — `DATABASE_URL` and `REDIS_URL` — are set
in Compose. `MERIDIAN_AUTO_MIGRATE` is deliberately `true`: upstream defaults
it off and then merely logs "Skipping database migrations", which in an
environment with no operator surfaces as relay errors rather than as a missing
step.
- **The relay boots without object storage.** Every `MERIDIAN_S3_*` value is
defaulted in `crates/meridian-relay/src/config.rs`, so no MinIO service is
deployed here. Blossom media upload and download therefore fail in these
environments while everything else works. That is a deliberate omission, not
an oversight — add a `meridian-minio/` directory when media is in scope.
- **`RESET_VOLUMES=1` is now much more destructive than it was.** It removes the
relay's Postgres volume and its git working tree alongside the Keycloak
database — every event in the environment. It remains the only way to reimport
a changed realm.
- **Exactly one host port is published**: `PROXY_EXPOSED_PORT:PROXY_INTERNAL_PORT`
— `7070:7070` local, `8080:8080` dev/staging. Keycloak (`8090`) and its
Postgres (`5432`) are `expose:` only, reachable on the Compose network alone.
So on a developer workstation this stack occupies **7070 and nothing else**.
- That 7070 is outside every `./run.sh` allocation walk in the parent
(`.settings/run-profiles/local.yaml` starts from 3003 / 5435 / 6380 / 8082 /
8093 / 9004 with `search_limit: 400`; no walk reaches 7070), so it needs no
entry in `allocation.reserved`. The Keycloak `8090` would collide with the
parent's upstream control-plane default if it were published — it is not.
- `EXPOSE ${PROXY_INTERNAL_PORT}` in the proxy Dockerfiles **expands to nothing**:
no `ARG`/`ENV` declares that name and no `--build-arg` is passed. Verified
harmless — the build succeeds and the image simply carries no `ExposedPorts` —
but the line documents nothing. Compose publishes the port explicitly, which
is what actually works.
### Known gaps
Referenced by name from `cicd/.env.dev` and `cicd/.env.staging`. These are open,
not resolved:
| Gap | Detail |
| --- | --- |
| Relay DB credentials are in git | `MERIDIAN_PG_PASSWORD` sits in the committed env files, which the deploy job copies to the host verbatim. Same posture the Keycloak credentials already had. Fix: append them from GitLab CI/CD variables in the deploy job, the way `IMAGE_PATH` is appended — and verify duplicate-key precedence in the resulting `.env` rather than assuming the later line wins |
| Three image paths are unverified | `ironbank.ilab.private:8082/meridian/meridian:main` (the assumed IHLC mirror of the relay image), `…/docker/postgres:17-alpine`, and `…/docker/dragonflydb/dragonfly:v1.39.0`. Every other base image here was already in use; these three have not been resolved against a live registry. Confirm before the first pipeline run |
| No CSP on `/` | The scaffold's `default-src 'none'` policy was written for text stubs and would block the served client bundle's WebSocket, `blob:` previews and remote avatars. The relay location carries transport headers only. ZAP will report this on staging, correctly |
| Media is not deployed | No MinIO service; Blossom upload/download fails while the rest works |
| Image tags carry no provenance | `:dev` / `:staging` move. The application repo tags annotated releases at the verified deployed commit; that guarantee does not survive into these environments |
### Keycloak realm contract
- The image is built twice (`kc.sh build` in a builder stage, then
`start --optimized`) with `KC_DB=postgres` and
**`KC_HTTP_RELATIVE_PATH=/auth`**, plus `KC_PROXY=edge`,
`KC_HTTP_ENABLED=true`, `KC_HOSTNAME_STRICT=false`,
`KC_HOSTNAME_STRICT_HTTPS=false`. Keycloak is therefore **only correct behind
the proxy**, and `/auth` is the only path the proxy forwards.
- Realm JSON is copied to `/opt/keycloak/data/import` and imported with
`-Dkeycloak.migration.strategy=IGNORE_EXISTING`. **A redeploy never updates an
existing realm.** Editing a realm file and redeploying is a silent no-op; the
change needs `RESET_VOLUMES=1` (destroys the DB) or a manual admin-console
edit (which the next volume reset then discards).
- Realm `dragon-egg-green` / display name "Dragon Egg Green" — the template's
realm, never renamed. Realm roles: `dragon-egg-green-administrator`,
`-power-user`, `-user`, plus the Keycloak defaults.
- Client `dragon-egg-green`: `openid-connect`, **public**, standard flow and
direct access grants enabled, `redirectUris: ["*"]`, `webOrigins: ["*"]`,
refresh tokens on.
- `dragon-egg-green-users-0.json` seeds four users — `admin`, `poweruser`,
`user`, `unprivileged` (the last with no realm roles). Passwords are
**PBKDF2-SHA256, 27500 iterations** in `secretData`, not plaintext.
- Tokens/sessions: access token 300 s, SSO idle 1800 s, SSO max 36000 s,
signature `RS256`, TOTP available, `sslRequired: external`,
`registrationAllowed: false`, **`bruteForceProtected: false`**, no
`passwordPolicy`.
- **Keycloak is deployed but not wired to the relay.** It answers at `/auth` and
nothing consumes it: the relay authenticates with Nostr keypairs over NIP-42
and reads no OIDC token. Wiring it in would add a second authorizer and a
mapping between it and the existing dated grant rows — refused by name in the
root Design Law ("No third shim", "Add attributes, not mechanisms"). Route
that through `consult-council-approval` (Security & Identity) before any code.
- The proxy sends `X-Forwarded-Proto/-Host/-Port` and blanks `Forwarded`,
`X-Forwarded-Prefix`, `X-Forwarded-Ssl`. The `local` variant sets all three
(direct client traffic); the `ilab` variant sets only `X-Forwarded-Host`
because an ELB is assumed to set the rest. Dev and staging also add the CSP /
HSTS / frame-options header block, repeated per `location` — deliberately, per
the in-file comment: an `add_header` inside a `location` discards the ones
inherited from `server`.
### Facts to carry into any decision to adopt this
Stated plainly because each would be a defect in a deployment, and none is one
in a template:
- `redirectUris: ["*"]` and `webOrigins: ["*"]` on a **public** client with
direct access grants: any origin can complete the flow and any page can read
the response. This is the single largest item.
- Brute-force protection off, no password policy, and seeded demo accounts whose
hashes are in the repo (hashed ≠ safe to ship — they are offline-crackable and
the usernames are guessable).
- `cicd/docker-compose.yml` hard-codes **plaintext credentials** used for dev
and staging alike: `KEYCLOAK_ADMIN`/`KEYCLOAK_ADMIN_PASSWORD` both
`adminkeycloak`, Postgres `keycloakuser`/`keycloakuser`. They are not
variables, so a deploy cannot override them from CI/CD secrets.
- The pre-commit hooks here (`detect-aws-credentials`, `detect-private-key`,
`detect-secrets`) do not catch any of the above.
- `LICENSE` is `TO BE PROVIDED`. The parent repo is MIT / © STELLAR with a
manifest list that CI enforces, so **copying any file from here into the
tracked tree without settling its licence breaks that contract.**
### Deltas this scaffold and the parent repo have to reconcile
| Concern | Here | R2D2-MERIDIAN |
| --- | --- | --- |
| Identity | Keycloak OIDC realm, username + password | Nostr keypairs, NIP-42 relay auth |
| Registry | JFrog / IronBank (`ironbank.ilab.*`) | `registry.r2d2.office.ilab.zone/meridian-*` |
| Deploy | scp + `docker compose` to `dev-docker-01` / `stg-docker-01` | `deploy/charts/` (Helm), `deploy/compose/` |
| CI | GitLab pipelines under `cicd/` | `.github/` workflows, `just ci` |
| Gates | SonarQube SAST, ZAP DAST baseline | `just check` — fmt, clippy, unwrap ratchet, kind/MIP/STELLAR guards |
| Edge | OpenResty forwarding `/auth` only | The relay's own narrow HTTP surface (NIP-11/05, `/events`, `/query`, Blossom, git) |
| Store | One Postgres, Keycloak's only | `meridian-db` Postgres as the system of record, Dragonfly for fan-out |
| Versioning | `:dev` / `:staging` moving tags | Annotated tags at the verified deployed commit (`RELEASING.md`) |
The one capability here with **no equivalent in the parent** is the OIDC realm.
Wiring it into any enforcement path adds a second authorizer and a mapping
between it and the existing grant rows — refused by name in the root Design Law
("No third shim", "Add attributes, not mechanisms"). If that is on the table,
route it through `consult-council-approval` (Security & Identity council) and
record the call in the Bead before writing code.
## Work Guidance
- **Infrastructure = config; clients = source.** A change to relay/Keycloak/
proxy/Postgres/Dragonfly Dockerfiles or Compose stays config-only here. A
change to desktop, web, mobile, or admin-web lands in the matching
`meridian-*` client directory and is built by the parent's recipes.
- **One directory per deployable.** A new *container* component gets a new
`meridian-<name>/` with `Dockerfile.{local,dev,staging}`, a service in
`cicd/docker-compose.yml`, a build entry in `docker-compose-build.yml`, build
jobs in both `pipeline-dev.yml` and `pipeline-stg.yml`, a `needs:` entry on
both deploy jobs, and its ports in all three env files. Miss one and the
failure is environment-specific, which is the expensive kind.
- **A client with no container still gets a directory** and a README saying
why — the way `meridian-desktop/` and `meridian-mobile/` do. Absence is not
documentation.
- **Licence before copying.** `LICENSE` here reads `TO BE PROVIDED` while the
application repo is MIT / © STELLAR with a CI-enforced manifest. Moving a file
in either direction without settling that is a real problem, not a formality.
- **Move `TAG`, container names and nginx upstreams in one change.** Same for a
port: env file, Compose, Dockerfile and conf, together.
- **Do not copy the wildcard-redirect client, the seeded users, or the hard-coded
admin credentials into anything that will run.**
- **Do not rename the realm if it is live anywhere.** A realm rename invalidates
every issued token and every client configuration keyed to it. If it is not
live, renaming `dragon-egg-green` → `meridian` is the honest fix for the
half-finished template rebrand — and it touches the realm JSON, the users
file, both filenames, and the realm roles together.
- A realm change that must survive a redeploy needs `RESET_VOLUMES=1` scheduled
deliberately, because `IGNORE_EXISTING` will otherwise swallow it silently.
## Verification
There is no test suite here, and nothing in the parent's `just ci` reads this
directory. What is checkable:
```bash
# 1. Every pipeline and Compose file parses
for f in .gitlab-ci.yml cicd/*.yml; do python3 -c "import yaml,sys;yaml.safe_load(open('$f'))" || echo "FAIL $f"; done
# 2. Compose resolves: 6 services, one published port, container names carry the tag
cd cicd && docker compose --env-file .env.local config
# 3. nginx config is valid. `nginx -t` resolves upstreams at parse time, so pin them —
# without --add-host it aborts on the first unreachable name and never checks the rest.
T=$(mktemp -d); cp meridian-proxy/nginx/local.conf "$T/default.conf"
cp meridian-proxy/nginx/x-forwarded-for.local.conf "$T/"
docker run --rm -v "$T:/etc/nginx/conf.d:ro" \
--add-host meridian-keycloak-local:127.0.0.1 --add-host meridian-relay-local:127.0.0.1 \
nginx:alpine nginx -t
# 4. Local build + run — the only path that uses docker-compose-build.yml
cd cicd && docker compose --env-file .env.local up -d --build
# 5. Proxy answers, and the relay is actually behind it
curl -s localhost:7070/health # proxy's own stub
curl -s -H 'Accept: application/nostr+json' localhost:7070 # relay NIP-11 document
curl -s -o /dev/null -w '%{http_code}\n' localhost:7070/auth # Keycloak
# 6. Realm export is parseable — a malformed one fails inside the container,
# as a stack trace in a log nobody reads
python3 -c "import json;json.load(open('meridian-keycloak/configuration/realms/dragon-egg-green-realm.json'))"
# 7. Secret scanners this repo pins
pre-commit run --all-files
```
Checks 1–3 pass as committed, and were run against this tree: all 8 YAML files
parse; Compose resolves to 6 services with `7070` as the only published port and
`DATABASE_URL`/`REDIS_URL` both naming real containers; all three nginx confs
report `syntax is ok`.
- **Checks 4–5 have not been run here** — they need `ironbank.ilab.zone` and the
relay image, neither reachable from this workstation. A failure at `FROM` off
the IHLC network says nothing about the Dockerfiles.
- **The WebSocket path is the one thing check 5 must actually confirm.** A relay
behind a proxy without `Upgrade`/`Connection` headers still answers NIP-11,
still passes `/health`, still serves the client bundle — and delivers no
events. Test with a real client or `websocat ws://localhost:7070`, not with
`curl` alone.
- Pipeline edits are validated by GitLab's CI lint, not by anything local. The
unpinned `ref: master` includes mean a green lint today can break tomorrow.
- In the application repo, `just check-stellar` must stay green **with this doc
indexed** in the root Child STELLAR Index. It is a normal child of the chain
now that the nested `.git` is gone; dropping the row is an `orphan-doc` error.
## Child STELLAR Index
The component directories moving in from the application root bring their own
docs with them; each becomes a child of this one rather than of the repo root.
| Child | Covers |
| --- | --- |
| `meridian-admin-web/AGENTS.md` | Read-only operator dashboard and its relay admin API contract |
| `meridian-desktop/AGENTS.md` | Tauri 2 + React 19 desktop app. Nested: `src/features/` (→ `features/agents/`), `src-tauri/`, `tests/` |
| `meridian-mobile/AGENTS.md` | Flutter app — Riverpod + hooks rules, file ceiling, safe `flutter` commands |
| `meridian-web/AGENTS.md` | Browser web client (repo browser + invite accept) served by the relay |

1
REMAPPING/LICENSE Normal file
View file

@ -0,0 +1 @@
TO BE PROVIDED

84
REMAPPING/README.md Normal file
View file

@ -0,0 +1,84 @@
# meridian
Delivery repository for **MERIDIAN** — *Multidomain Exchange for Realtime
Integration of Data Innovation with Allied Nations* — in the IHLC environments.
This repository holds **deployment configuration only**. The application source
(relay, desktop, mobile, CLI, agent harness) lives in `R2D2-MERIDIAN` on
`git.office.ilab.zone`, and the container images it publishes are what the
directories here re-tag and deploy. One top-level directory per deployable:
| Directory | Delivers | Deployed as a container |
| --- | --- | --- |
| `meridian-relay` | The relay — Nostr WebSocket, HTTP surface, git, web + admin-web bundles | yes |
| `meridian-postgresql` | The relay's Postgres — system of record for events | yes |
| `meridian-dragonfly` | Pub/sub fan-out, presence, rate limits (Redis wire protocol) | yes |
| `meridian-keycloak` | Keycloak + realm import | yes |
| `meridian-keycloak-postgresql` | Keycloak's own database | yes |
| `meridian-proxy` | OpenResty reverse proxy — the only service published to the host | yes |
| `meridian-desktop` | Tauri desktop client (installable artifact) | no |
| `meridian-mobile` | Flutter mobile client (installable artifact) | no |
`cicd/` holds the GitLab pipelines, the Compose deployment and one environment
file per environment. `AGENTS.md` is the contract for changing any of it.
## Quickstart
### Building in local developer environment
```bash
cd cicd
cp .env.local .env
docker compose up -d --build # COMPOSE_FILE in .env adds the build overlay
curl -s localhost:7070/health # proxy answers
curl -s -H 'Accept: application/nostr+json' localhost:7070 # relay NIP-11 document
```
`7070` is the only host port taken, chosen so this stack and a local
`R2D2-MERIDIAN` checkout can run side by side. Base images come from IronBank,
so the build needs network reach to `ironbank.ilab.zone`.
Tear down with `docker compose down`. Adding `--volumes` destroys the relay's
Postgres volume and its git working tree as well as the Keycloak database.
### Building in IH environments
Push to a branch; the pipeline does the rest.
| Branch | Builds with | Deploys to | URL |
| --- | --- | --- | --- |
| `dev` | `Dockerfile.dev`, `cicd/.env.dev` | `dev-docker-01` | https://meridian-dev.ilab.zone/ |
| `staging` | `Dockerfile.staging`, `cicd/.env.staging` | `stg-docker-01` | https://meridian-stg.ilab.zone/ |
Merge requests run SonarQube only. Staging additionally runs an OWASP ZAP
baseline scan. Setting the CI/CD variable `RESET_VOLUMES=1` reinitialises the
environment from scratch — including every event stored in it.
## Technical stack
- **Relay** — Rust, published as a container by the application repository and
re-tagged here. Speaks Nostr (NIP-01/NIP-29/NIP-42) over WebSocket plus a
narrow HTTP surface.
- **Postgres 17** — the system of record for events. Migrations are applied by
the relay on boot (`MERIDIAN_AUTO_MIGRATE=true`, baked into the image).
- **Dragonfly** — Redis wire protocol; pub/sub fan-out, presence, rate limits.
- **Keycloak 22** — OIDC, realm `dragon-egg-green`, reachable at `/auth`. Not
wired into the relay's authorization path; see `AGENTS.md`.
- **OpenResty** — reverse proxy and the only published service.
- Clients: Tauri 2 + React 19 (desktop), Flutter (mobile). Built in the
application repository, not by these pipelines.
## Visible endpoints
Everything is served through the proxy on one port — `7070` locally, `8080` in
the IH environments (behind TLS at the environment URL).
| Path | Serves |
| --- | --- |
| `/health` | Proxy liveness. Answered by nginx itself, never forwarded |
| `/auth` | Keycloak |
| `/` | The relay: Nostr WebSocket upgrade, NIP-11 with `Accept: application/nostr+json`, NIP-05, `POST /events`, `/query`, `/count`, Blossom media, git smart HTTP, workflow webhooks at `/hooks/{id}`, and the web + admin-web bundles |
The relay's readiness probe (`/_readiness`, port 8080) and metrics (9102) are
**not** proxied — they are reachable only inside the Compose network, which is
where the container healthcheck runs.

56
REMAPPING/cicd/.env.dev Normal file
View file

@ -0,0 +1,56 @@
########################################################################################################################
#
# 'docker compose' and 'docker build' variables for IL DEVELOPMENT environment:
# 1. Used to set up 'docker compose' integration variables (ports, tag, image path) for IL DEVELOPMENT environment.
# 2. Used to set up 'docker build' integration variables (mostly EXPOSE port by image, but could be more).
# 3. Generic rule on setup:
# - use dockerfile to customize the image
# - used docker-compose.yml to customize the deployment
# 4. All the values are strongly connected with docker-compose.yml, dockerfiles of the images
# and pipeline configuration. See there before changing.
#
########################################################################################################################
# Generic variables
# Docker project name
COMPOSE_PROJECT_NAME=meridian
# Specification of docker-compose files to be used
COMPOSE_PATH_SEPARATOR=:
COMPOSE_FILE=docker-compose.yml
# Tag used to mark images and name containers and other elements
TAG=dev
# Docker repository address
DOCKER_REPOSITORY=ironbank.ilab.private:8082
# meridian-relay
RELAY_INTERNAL_PORT=3000
RELAY_HEALTH_INTERNAL_PORT=8080
# meridian-postgresql (the relay's system of record — NOT the Keycloak database)
MERIDIAN_POSTGRESQL_INTERNAL_PORT=5432
MERIDIAN_PG_DB=meridian
MERIDIAN_PG_USER=meridian
# KNOWN GAP: this file is committed and copied to the host verbatim by the deploy job, so this
# value is not a secret. It matches the posture the Keycloak credentials in docker-compose.yml
# already have. Replace both with GitLab CI/CD variables appended at deploy time — tracked in
# AGENTS.md § Known gaps — before this environment holds anything that matters.
MERIDIAN_PG_PASSWORD=meridian_dev
# meridian-dragonfly (Redis wire protocol — pub/sub, presence, rate limits)
MERIDIAN_DRAGONFLY_INTERNAL_PORT=6379
DRAGONFLY_MAXMEMORY=1gb
DRAGONFLY_THREADS=2
# meridian-keycloak
KEYCLOAK_INTERNAL_PORT=8090
# meridian-keycloak-postgresql
KEYCLOAK_POSTGRESQL_INTERNAL_PORT=5432
# meridian-proxy
PROXY_INTERNAL_PORT=8080
PROXY_EXPOSED_PORT=8080

View file

@ -0,0 +1,56 @@
########################################################################################################################
#
# 'docker compose' and 'docker build' variables for IL STAGING environment:
# 1. Used to set up 'docker compose' integration variables (ports, tag, image path) for IL STAGING environment.
# 2. Used to set up 'docker build' integration variables (mostly EXPOSE port by image, but could be more).
# 3. Generic rule on setup:
# - use dockerfile to customize the image
# - used docker-compose.yml to customize the deployment
# 4. All the values are strongly connected with docker-compose.yml, dockerfiles of the images
# and pipeline configuration. See there before changing.
#
########################################################################################################################
# Generic variables
# Docker project name
COMPOSE_PROJECT_NAME=meridian
# Specification of docker-compose files to be used
COMPOSE_PATH_SEPARATOR=:
COMPOSE_FILE=docker-compose.yml
# Tag used to mark images and name containers and other elements
TAG=staging
# Docker repository address
DOCKER_REPOSITORY=ironbank.ilab.private:8082
# meridian-relay
RELAY_INTERNAL_PORT=3000
RELAY_HEALTH_INTERNAL_PORT=8080
# meridian-postgresql (the relay's system of record — NOT the Keycloak database)
MERIDIAN_POSTGRESQL_INTERNAL_PORT=5432
MERIDIAN_PG_DB=meridian
MERIDIAN_PG_USER=meridian
# KNOWN GAP: this file is committed and copied to the host verbatim by the deploy job, so this
# value is not a secret. Staging is the environment ZAP scans and the one closest to real use —
# replace this with a GitLab CI/CD variable appended at deploy time before it holds real data.
# Tracked in AGENTS.md § Known gaps.
MERIDIAN_PG_PASSWORD=meridian_dev
# meridian-dragonfly (Redis wire protocol — pub/sub, presence, rate limits)
MERIDIAN_DRAGONFLY_INTERNAL_PORT=6379
DRAGONFLY_MAXMEMORY=1gb
DRAGONFLY_THREADS=2
# meridian-keycloak
KEYCLOAK_INTERNAL_PORT=8090
# meridian-keycloak-postgresql
KEYCLOAK_POSTGRESQL_INTERNAL_PORT=5432
# meridian-proxy
PROXY_INTERNAL_PORT=8080
PROXY_EXPOSED_PORT=8080

0
REMAPPING/cicd/.gitkeep Normal file
View file

View file

@ -0,0 +1,44 @@
---
version: "3.9"
########################################################################################################################
#
# These are additional build step for docker compose:
# 1. To be used only in local developer environment (see Readme).
# 2. Within IL environments docker images are built using GitLab CI/CD pipeline.
#
# One entry per deployable directory. meridian-desktop and meridian-mobile are absent on purpose:
# they ship installable artifacts, not containers.
#
########################################################################################################################
services:
meridian-relay:
build:
context: ./../meridian-relay
dockerfile: Dockerfile.${TAG}
meridian-postgresql:
build:
context: ./../meridian-postgresql
dockerfile: Dockerfile.${TAG}
meridian-dragonfly:
build:
context: ./../meridian-dragonfly
dockerfile: Dockerfile.${TAG}
meridian-keycloak:
build:
context: ./../meridian-keycloak
dockerfile: Dockerfile.${TAG}
meridian-keycloak-postgresql:
build:
context: ./../meridian-keycloak-postgresql
dockerfile: Dockerfile.${TAG}
meridian-proxy:
build:
context: ./../meridian-proxy
dockerfile: Dockerfile.${TAG}

View file

@ -0,0 +1,149 @@
---
version: "3.9"
########################################################################################################################
#
# These are deployment steps for docker compose:
# 1. Used for both: local development environment and IL environments.
# 2. This configuration is also used to set up deployment variables for containers.
#
# Service grouping mirrors the top-level directories: one directory per deployable, one service
# per directory. Directories with no service here (meridian-desktop, meridian-mobile) ship
# installable artifacts rather than containers — see their READMEs.
#
# WARNING: the deploy jobs run 'docker compose down --remove-orphans', and with CI/CD variable
# RESET_VOLUMES=1 they add '--volumes'. That now destroys the RELAY's Postgres volume and its git
# working tree as well as the Keycloak database. It is the documented way to reimport a realm;
# it is also the way to lose every event in the environment.
#
########################################################################################################################
services:
# --------------------------------------------------------------------------------------------
# Application
# --------------------------------------------------------------------------------------------
meridian-relay:
image: ${IMAGE_PATH}meridian-relay:${TAG}
container_name: meridian-relay-${TAG}
restart: always
environment:
# Host names carry ${TAG}, matching container_name. The proxy's nginx conf hard-codes the
# same names; change one and you must change the other, per environment.
DATABASE_URL: postgres://${MERIDIAN_PG_USER}:${MERIDIAN_PG_PASSWORD}@meridian-postgresql-${TAG}:${MERIDIAN_POSTGRESQL_INTERNAL_PORT}/${MERIDIAN_PG_DB}
REDIS_URL: redis://meridian-dragonfly-${TAG}:${MERIDIAN_DRAGONFLY_INTERNAL_PORT}
# Listeners, git path and MERIDIAN_AUTO_MIGRATE are baked into the image
# (meridian-relay/Dockerfile.*), so they cannot drift per environment.
expose:
- "${RELAY_INTERNAL_PORT}"
- "${RELAY_HEALTH_INTERNAL_PORT}"
volumes:
- meridian_git_data:/data/git
# The runtime image has bash but no curl or wget, so the probe opens /dev/tcp directly.
healthcheck:
test:
[
"CMD-SHELL",
"bash -ec 'exec 3<>/dev/tcp/127.0.0.1/${RELAY_HEALTH_INTERNAL_PORT}; printf \"GET /_readiness HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\nConnection: close\\r\\n\\r\\n\" >&3; grep -q \"200 OK\" <&3'",
]
interval: 10s
timeout: 3s
retries: 12
start_period: 30s
depends_on:
- meridian-postgresql
- meridian-dragonfly
meridian-postgresql:
image: ${IMAGE_PATH}meridian-postgresql:${TAG}
container_name: meridian-postgresql-${TAG}
restart: always
volumes:
- meridian_postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${MERIDIAN_PG_DB}
POSTGRES_USER: ${MERIDIAN_PG_USER}
POSTGRES_PASSWORD: ${MERIDIAN_PG_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
expose:
- "${MERIDIAN_POSTGRESQL_INTERNAL_PORT}"
meridian-dragonfly:
image: ${IMAGE_PATH}meridian-dragonfly:${TAG}
container_name: meridian-dragonfly-${TAG}
restart: always
# Dragonfly mlocks its arenas; without this it refuses to start on most default daemons.
ulimits:
memlock: -1
command:
- "--maxmemory=${DRAGONFLY_MAXMEMORY}"
- "--proactor_threads=${DRAGONFLY_THREADS}"
# Empty dbfilename disables snapshotting: this tier is a cache, never a system of record.
- "--dbfilename="
expose:
- "${MERIDIAN_DRAGONFLY_INTERNAL_PORT}"
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
# --------------------------------------------------------------------------------------------
# Identity (template residue — not wired to the relay; see AGENTS.md)
# --------------------------------------------------------------------------------------------
meridian-keycloak:
image: ${IMAGE_PATH}meridian-keycloak:${TAG}
container_name: meridian-keycloak-${TAG}
restart: always
command:
[
"-Dkeycloak.migration.action=import",
"-Dkeycloak.migration.provider=dir",
"-Dkeycloak.migration.dir=/opt/keycloak/data/import",
"-Dkeycloak.migration.strategy=IGNORE_EXISTING"
]
environment:
KC_DB_URL: jdbc:postgresql://meridian-keycloak-postgresql-${TAG}/keycloak_db
KC_DB_USERNAME: keycloakuser
KC_DB_PASSWORD: keycloakuser
KEYCLOAK_ADMIN: adminkeycloak
KEYCLOAK_ADMIN_PASSWORD: adminkeycloak
KC_HTTP_PORT: ${KEYCLOAK_INTERNAL_PORT}
expose:
- "${KEYCLOAK_INTERNAL_PORT}"
depends_on:
- meridian-keycloak-postgresql
meridian-keycloak-postgresql:
image: ${IMAGE_PATH}meridian-keycloak-postgresql:${TAG}
container_name: meridian-keycloak-postgresql-${TAG}
restart: always
volumes:
- keycloak_postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak_db
POSTGRES_USER: keycloakuser
POSTGRES_PASSWORD: keycloakuser
expose:
- "${KEYCLOAK_POSTGRESQL_INTERNAL_PORT}"
# --------------------------------------------------------------------------------------------
# Edge — the only service published to the host
# --------------------------------------------------------------------------------------------
meridian-proxy:
image: ${IMAGE_PATH}meridian-proxy:${TAG}
container_name: meridian-proxy-${TAG}
restart: always
ports:
- "${PROXY_EXPOSED_PORT}:${PROXY_INTERNAL_PORT}"
depends_on:
- meridian-relay
- meridian-keycloak
volumes:
keycloak_postgres_data:
meridian_postgres_data:
meridian_git_data:

View file

@ -0,0 +1,85 @@
---
########################################################################################################################
#
# This configuration file is dedicated to define common to all pipelines elements:
# 1. Set up repeating base steps, which are further used (by extending them) in DEV and STAGING configuration.
# 2. Common steps to all the pipelines.
#
########################################################################################################################
# Generic, maven based, backend compilation step
.compile-backend-base:
image: $DOCKER_REPOSITORY/docker/maven:3.8.4-openjdk-17
before_script:
# Setting up Maven repository to point out to IronBank
- echo -n $JF_MAVEN_SETTINGS > $HOME/.m2/settings.xml
script:
# Change the folder to the Maven project (folder with root pom.xml)
- cd maven-project-folder
# Run compilation and tests
- mvn $MAVEN_PROFILES $MAVEN_PHASES
artifacts:
paths:
# Path from root project path to the build artifacts
- maven-project-folder/target/*
# Generic Node.js compilation job
.compile-dragonegg-frontend-base:
image: $DOCKER_REPOSITORY/docker/node:15.6
before_script:
# Do not treat warnings as errors
- CI=false
# Set up JFrog repository
- npm config set registry http://${JF_NPM_PROXY_REGISTRY}
- npm config set '//${JF_NPM_PROXY_REGISTRY}:_authToken' '${JF_ACCESS_TOKEN}'
- npm config set always-auth true
- npm config set loglevel verbose
script:
# Change the folder to the npm project (folder with package.json)
- cd npm-project-folder
# Install npm dependencies
- npm install
# Build application
- npm run build:$NODE_PROFILE
artifacts:
paths:
# Path from root project path to the build artifacts
- npm-project-folder/build/*
# Generic docker build step (used to build all the images)
.build-image-base:
script:
# Going to building folder
- cd $SERVICE_FOLDER
# Copying environment file
- cp $ENV_FILE .env
# Setting up the environment
- export $(grep -v '^#' .env | xargs)
# JFrog docker proxy registry login (to pull base images)
- echo -n $JF_PASSWORD | docker login -u $JF_USERNAME --password-stdin $JF_DOCKER_PROXY_REGISTRY
# JFrog docker project registry login (to push built images)
- echo -n $JF_PASSWORD | docker login -u $JF_USERNAME --password-stdin $JF_DOCKER_PROJECT_REGISTRY
# Build docker image
- docker build -f $DOCKER_FILE -t $JF_DOCKER_PROJECT_REGISTRY/$SERVICE_FOLDER:$TAG .
# Push docker image to the JFrog project registry
- docker push $JF_DOCKER_PROJECT_REGISTRY/$SERVICE_FOLDER:$TAG
# Logout from JFrog docker proxy registry
- docker logout $JF_DOCKER_PROXY_REGISTRY
# Logout from JFrog docker project registry
- docker logout $JF_DOCKER_PROJECT_REGISTRY
# Image cleanup
remove-images:
stage: cleanup
script:
# remove all tagged images
- docker rmi -f $(docker images --filter reference="$JF_DOCKER_PROJECT_REGISTRY/*" -q) || true
# frontend build produces a dangling image by using multistage build
# remove dangling images but keep newer than 4h (to make frequent builds faster)
- docker image prune -f --filter until="4h" || true
# If multistage build fails there might be an exited container left. Remove all exited containers.
- docker rm $(docker ps -a -f status=exited -q) || true
rules:
- if: '$CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "staging" || $CI_COMMIT_BRANCH == "master"'
tags:
- docker

View file

@ -0,0 +1,141 @@
---
########################################################################################################################
#
# Development environment pipeline configuration:
# 1. Set up all the phases with respect of development environment specifics.
# 2. Building step use 'docker build', proper dockerfile of the image and variables defined in the environment file.
# 3. Deploying steps utilize 'docker compose', docker-compose.yml file as configuration and variables defined in
# the environment file.
#
########################################################################################################################
########################################################################################################################
#
# Building phase elements
#
########################################################################################################################
# Extended generic building image stage for DEV environment (used to build all DEV images)
.build-image-base-dev:
extends: .build-image-base
stage: build
variables:
ENV_FILE: ../cicd/.env.dev
DOCKER_FILE: Dockerfile.dev
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
tags:
- docker
# Building image of the meridian relay.
# This does not compile Rust — it re-tags the image published by the application repository and
# bakes the runtime shape. See meridian-relay/Dockerfile.dev.
build-meridian-relay-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-relay
needs: []
# Building image of the relay's PostgreSQL — the system of record for events.
# Distinct from the Keycloak database below: one instance, one migration history.
build-meridian-postgresql-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-postgresql
needs: []
# Building image of Dragonfly — pub/sub fan-out, presence and rate limits over the Redis wire
# protocol. Do not substitute a 'redis' service; the application refuses one by name.
build-meridian-dragonfly-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-dragonfly
needs: []
# Building image of meridian Keycloak PostgreSQL
build-meridian-keycloak-postgresql-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-keycloak-postgresql
needs: []
# Building image of meridian Keycloak
build-meridian-keycloak-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-keycloak
needs:
- build-meridian-keycloak-postgresql-dev
# Building image of DragonEgg OpenResty reverse proxy
build-meridian-proxy-dev:
extends: .build-image-base-dev
variables:
SERVICE_FOLDER: meridian-proxy
needs: []
########################################################################################################################
#
# Deployment phase elements
#
########################################################################################################################
# Deploying to project's dev docker
deploy-dev:
stage: deploy
environment:
name: meridian-dev
url: https://meridian-dev.ilab.zone/
before_script:
- eval $(ssh-agent -s)
- echo "$DEV_SSH_KEY" | base64 -d | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER "mkdir -p /home/$USER/meridian"
- scp -o StrictHostKeyChecking=no ./cicd/docker-compose.yml $USER@$DEV_DOCKER:/home/$USER/meridian
- scp -o StrictHostKeyChecking=no ./cicd/.env.dev $USER@$DEV_DOCKER:/home/$USER/meridian/.env
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER
"echo 'IMAGE_PATH='$JF_DOCKER_PROJECT_REGISTRY/ >> /home/$USER/meridian/.env"
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER
"echo -n $JF_PASSWORD | docker login -u $JF_USERNAME --password-stdin $JF_DOCKER_PROJECT_REGISTRY"
# Set up CI/CD variable RESET_VOLUMES to 1 to remove the volumes of the MVP (to reinitiate MVP from scratch)
- if [ "$RESET_VOLUMES" == "1" ]; then
ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER
"cd /home/$USER/meridian && docker compose down --remove-orphans --volumes";
else
ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER
"cd /home/$USER/meridian && docker compose down --remove-orphans";
fi
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER
"docker rmi -f \$(docker images --filter reference="$JF_DOCKER_PROJECT_REGISTRY/*" -q) || true"
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER "cd /home/$USER/meridian && docker compose up -d"
- ssh -o StrictHostKeyChecking=no $USER@$DEV_DOCKER "docker logout $JF_DOCKER_PROJECT_REGISTRY"
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
tags:
- docker
needs:
- build-meridian-relay-dev
- build-meridian-postgresql-dev
- build-meridian-dragonfly-dev
- build-meridian-keycloak-dev
- build-meridian-proxy-dev
########################################################################################################################
#
# Testing phase elements
#
########################################################################################################################
# SAST using SonarQube of the dev branch (this job also prepares and configures the SonarQube)
sonar-meridian-dev:
extends: .template_sast_sonarqube_dev
# Specify the pipeline stage which SAST shall be run in.
# It is recommended to define a dedicated stage for SAST scanning
stage: sast
# Make the dependency on the job compiling the software
needs: []
only:
# This line indicates that the job will be triggered against dev branch only
- dev

View file

@ -0,0 +1,27 @@
---
# Compilation of backend - if Java based, must be compiled before SAST
# compile-backend-feature-branch:
# extends: .compile-backend-base
# stage: compile
# variables:
# MAVEN_PHASES: clean package
# MAVEN_PROFILES: " "
# rules:
# - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# tags:
# - docker
# needs: []
# SAST using SonarQube of the feature branch (merge request must be created to make it running)
sonar-mvp-feature-branch:
extends: .template_sast_sonarqube_feature_branch
# Specify the pipeline stage which SAST shall be run in.
# It is recommended to define a dedicated stage for SAST scanning
stage: sast
needs: []
# needs:
# # Make the dependency on the job compiling the software
# - compile-backend-feature-branch
only:
# This line indicates that the job will be triggered against merge requests
- merge_requests

View file

@ -0,0 +1,207 @@
---
########################################################################################################################
#
# Staging environment pipeline configuration:
# 1. Set up all the phases with respect of staging environment specifics.
# 2. Building step use 'docker build', proper dockerfile of the image and variables defined in the environment file.
# 3. Deploying steps utilize 'docker compose', docker-compose.yml file as configuration and variables defined in
# the environment file.
#
########################################################################################################################
########################################################################################################################
#
# Building phase elements
#
########################################################################################################################
# Extended generic building image stage for STAGING environment (used to build all STAGING images)
.build-image-base-stg:
extends: .build-image-base
stage: build
variables:
ENV_FILE: ../cicd/.env.staging
DOCKER_FILE: Dockerfile.staging
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
tags:
- docker
# Building image of the meridian relay.
# This does not compile Rust — it re-tags the image published by the application repository and
# bakes the runtime shape. See meridian-relay/Dockerfile.staging.
build-meridian-relay-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-relay
needs: []
# Building image of the relay's PostgreSQL — the system of record for events.
# Distinct from the Keycloak database below: one instance, one migration history.
build-meridian-postgresql-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-postgresql
needs: []
# Building image of Dragonfly — pub/sub fan-out, presence and rate limits over the Redis wire
# protocol. Do not substitute a 'redis' service; the application refuses one by name.
build-meridian-dragonfly-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-dragonfly
needs: []
# Building image of meridian Keycloak PostgreSQL
build-meridian-keycloak-postgresql-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-keycloak-postgresql
needs: []
# Building image of meridian Keycloak
build-meridian-keycloak-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-keycloak
needs:
- build-meridian-keycloak-postgresql-stg
# Building image of the meridian OpenResty reverse proxy
build-meridian-proxy-stg:
extends: .build-image-base-stg
variables:
SERVICE_FOLDER: meridian-proxy
needs: []
########################################################################################################################
#
# Deployment phase elements
#
########################################################################################################################
# Deploying to project's staging docker
deploy-stg:
stage: deploy
environment:
name: meridian-stg
url: https://meridian-stg.ilab.zone/
before_script:
- eval $(ssh-agent -s)
- echo "$STG_SSH_KEY" | base64 -d | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER "mkdir -p /home/$USER/meridian"
- scp -o StrictHostKeyChecking=no ./cicd/docker-compose.yml $USER@$STG_DOCKER:/home/$USER/meridian
- scp -o StrictHostKeyChecking=no ./cicd/.env.staging $USER@$STG_DOCKER:/home/$USER/meridian/.env
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER
"echo 'IMAGE_PATH='$JF_DOCKER_PROJECT_REGISTRY/ >> /home/$USER/meridian/.env"
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER
"echo -n $JF_PASSWORD | docker login -u $JF_USERNAME --password-stdin $JF_DOCKER_PROJECT_REGISTRY"
# Set up CI/CD variable RESET_VOLUMES to 1 to remove the volumes of the MVP (to reinitiate MVP from scratch)
- if [ "$RESET_VOLUMES" == "1" ]; then
ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER
"cd /home/$USER/meridian && docker compose down --remove-orphans --volumes";
else
ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER
"cd /home/$USER/meridian && docker compose down --remove-orphans";
fi
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER
"docker rmi -f \$(docker images --filter reference="$JF_DOCKER_PROJECT_REGISTRY/*" -q) || true"
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER "cd /home/$USER/meridian && docker compose up -d"
- ssh -o StrictHostKeyChecking=no $USER@$STG_DOCKER "docker logout $JF_DOCKER_PROJECT_REGISTRY"
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
tags:
- docker
needs:
- build-meridian-relay-stg
- build-meridian-postgresql-stg
- build-meridian-dragonfly-stg
- build-meridian-keycloak-stg
- build-meridian-proxy-stg
########################################################################################################################
#
# Testing phase elements
#
########################################################################################################################
# SAST using SonarQube of the staging branch
sonar-meridian-stg:
extends: .template_sast_sonarqube_staging
# Specify the pipeline stage which SAST shall be run in.
# It is recommended to define a dedicated stage for SAST scanning
stage: sast
# Make the dependency on the job compiling the software
needs: []
only:
# This line indicates that the job will be triggered against staging branch only
- staging
# OWASP ZAP DAST - baseline scan
# ZAP is performing baseline scan, fast scan, not doing any advanced attacs
# (more here: https://www.zaproxy.org/docs/docker/baseline-scan/)
zap-meridian-baseline:
extends: .template_dast_zap_baseline
# Specify the pipeline stage which DAST shall be run in.
# It is recommended to define a dedicated stage for DAST scanning
# (not named dast, as GitLab already use such stage globally)
stage: dasttest
variables:
# Specify the web application URL the scanning should start with
ZAP_URL: https://meridian-stg.ilab.zone
rules:
# Specify which pipeline shall this job be fired in, by indicating the rule on branch.
- if: '$CI_COMMIT_BRANCH == "staging"'
tags:
- docker
needs:
# Make the dependency on the job deploying the application - DAST needs working application
- deploy-stg
# OWASP ZAP DAST - full scan
# ZAP is performing full scan, first passive spidering, than predefined attacs
# (more here: https://www.zaproxy.org/docs/docker/full-scan/)
# WARNING!!! This task could run very long and may damage the application,
# as actual attacks are run by ZAP - be careful!
# zap-meridian-full:
# extends: .template_dast_zap_fullscan
# # Specify the pipeline stage which DAST shall be run in.
# # It is recommended to define a dedicated stage for DAST scanning
# # (not named dast, as GitLab already use such stage globally)
# stage: dasttest
# variables:
# # Specify the web application URL the scanning should start with
# ZAP_URL: https://meridian-stg.ilab.zone
# rules:
# # Specify which pipeline shall this job be fired in, by indicating the rule on branch.
# - if: '$CI_COMMIT_BRANCH == "staging"'
# tags:
# - docker
# needs:
# # Make the dependency on the job deploying the application - DAST needs working application
# - deploy-stg
# OWASP ZAP DAST - API scan
# ZAP is performing scans against APIs defined by OpenAPI, SOAP, or GraphQL via URL
# (more here: https://www.zaproxy.org/docs/docker/api-scan/).
# zap-meridian-backend-api-scan:
# extends: .template_dast_zap_apiscan
# # Specify the pipeline stage which DAST shall be run in.
# # It is recommended to define a dedicated stage for DAST scanning
# # (not named dast, as GitLab already use such stage globally)
# stage: dasttest
# variables:
# # One of: openapi, soap, graphql
# ZAP_API_FORMAT: openapi
# # URL pointing to OpenAPI specification of the API
# ZAP_URL: https://meridian-stg.ilab.zone/api/api-docs
# rules:
# - if: '$CI_COMMIT_BRANCH == "staging"'
# tags:
# - docker
# needs:
# # Make the dependency on the job deploying the application - DAST needs working application
# - deploy-stg

View file

@ -0,0 +1,53 @@
---
########################################################################################################################
#
# Main entry to the GitLab Ci/CD pipelines configuration:
# 1. Configures the stages.
# 2. Configures pipeline running conditions.
# 3. Configures variables to be used globally in all configuration files.
#
########################################################################################################################
include:
# ZAP
- project: 'ihlc/teams/team-nightwatch/servicestream/configuration-templates'
ref: master
file: 'cicd/template-dast-zap.yml'
# SonarQube
- project: 'ihlc/teams/team-nightwatch/servicestream/configuration-templates'
ref: master
file: 'cicd/template-sast-sonarqube.yml'
- cicd/pipeline-common.yml
- cicd/pipeline-dev.yml
- cicd/pipeline-stg.yml
- cicd/pipeline-feature-branch.yml
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web"'
stages:
- compile
- sast
- build
- deploy
- apitest
- dasttest
- cleanup
variables:
USER: developer
DEV_SSH_KEY: $MERIDIAN_DEV_DOCKER_PEM
STG_SSH_KEY: $MERIDIAN_STG_DOCKER_PEM
DEV_DOCKER: dev-docker-01
STG_DOCKER: stg-docker-01
SONARQUBE_URL: 'https://sonarqube.ilab.zone/'
SONARQUBE_TOKEN: $SONARQUBE_MASTER_TOKEN
# Project key must follow the pattern <team name>-<project code> (example: team-alpha-AWARE)
# Must be the same like in project's sonar-project.properties file (if defined there)
SONARQUBE_PROJECT_KEY: team-alpha-meridian
# Global variables setting up the authenticated access to the IronBank from the pipelines
# (to pull the images which are used in the pipelines to execute the jobs)
DOCKER_REPOSITORY: ironbank.ilab.private:8082
DOCKER_AUTH_CONFIG: '{"auths":{"$JF_DOCKER_PROXY_REGISTRY":{"auth":"$JF_CREDENTIALS_BASE64"}}}'

View file

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 5" shape-rendering="crispEdges">
<!-- Meridian symbol: 4x5 pixel-art C. Geometry matches
desktop/src/shared/ui/meridian-logo/MeridianSymbol.tsx (MERIDIAN_GLYPH). -->
REMAPPING/meridian-desktop/src/shared/ui/meridian-logo/MeridianSymbol.tsx (MERIDIAN_GLYPH). -->
<style>
.mark { fill: #231e1e; }
@media (prefers-color-scheme: dark) {

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 468 B

Before After
Before After

View file

@ -582,7 +582,7 @@ function formatBytes(bytes: number) {
/**
* The Meridian bracket symbol. Geometry matches
* `desktop/src/shared/ui/meridian-logo/MeridianSymbol.tsx` (tight cut) —
* `REMAPPING/meridian-desktop/src/shared/ui/meridian-logo/MeridianSymbol.tsx` (tight cut) —
* keep the two in step. Strokes in `currentColor`, so `.brand-mark` tints it.
*/
function MeridianMark() {

View file

@ -20,7 +20,7 @@ and every user-facing surface that is not the web repo browser or mobile.
| `scripts/app-icon/` | The brand raster generators — every app icon, agent avatar, mobile splash mark and the macOS DMG background across **all** clients |
| `tailwind.config.js` | The type scale, including the `text-2xs` / `text-3xs` meta tokens |
Biome handles lint and format for all of `desktop/`.
Biome handles lint and format for all of `REMAPPING/meridian-desktop/`.
## Local Contracts
@ -53,7 +53,7 @@ non-obvious ones, and where they live:
`meridian-46z4`, `.settings/features/feature-relay-discovery.md`.
- **`src/shared/constants/kinds.ts` mirrors `crates/meridian-core/src/kind.rs`.** It
is not an independent source of truth; changing a kind means changing both (and
`mobile/lib/shared/relay/nostr_models.dart`).
`REMAPPING/meridian-mobile/lib/shared/relay/nostr_models.dart`).
- **The build stamp is baked at build time, never hardcoded.** `vite.config.ts`
resolves the git commit, dirty flag, channel (release-tag-on-HEAD or `dev`),
and time into `define`d literals; `src/shared/lib/buildInfo.ts` reads them
@ -198,11 +198,11 @@ non-obvious ones, and where they live:
`MeridianGlobe` and `ParticleText` are only React bindings over them. Two
other surfaces consume the modules directly: the control plane bundles them
into the inlined brand asset its Rust-served pages carry
(`scripts/generate-control-plane-brand.mjs`), and `web/` imports them through
(`scripts/generate-control-plane-brand.mjs`), and `REMAPPING/meridian-web/` imports them through
its `@brand` alias. So keep them framework-free — no React, no DOM creation,
no module-level state — and re-run that generator after touching either, or
the control-plane pages keep drawing the previous build. `just check-brand`
fails on the drift. `web/` needs no regeneration; it builds from source.
fails on the drift. `REMAPPING/meridian-web/` needs no regeneration; it builds from source.
This is also why `landOutline.ts` is generated to **one** target: the other
two surfaces resolve to this copy rather than keeping their own.
- `routeTree.gen.ts` is generated. Do not hand-edit it.
@ -225,7 +225,7 @@ pnpm --dir desktop exec vite --config vite.preview.config.ts
bundle* — and `tauri dev` emits a bare binary, not a bundle, so it registers
nothing. Every deep link ("Open in Meridian" on the control-plane relays page,
`meridian://add-community`, `meridian://nostr-bind`) therefore launches
`desktop/src-tauri/target/release/bundle/macos/Meridian.app`, whatever that
`src-tauri/target/release/bundle/macos/Meridian.app`, whatever that
happens to contain. The symptom is "the deep link opened an old app": it did,
and rebuilding the dev server does not touch it. Rebuild the bundle with
**`./run.sh bundle`** — and only that, because a GUI-launched `.app` inherits

View file

@ -1,5 +1,5 @@
{
"extends": ["../biome.json"],
"extends": ["../../biome.json"],
"files": {
"includes": ["!**/*.generated.json", "!src/features/canvas/vendor"]
}

View file

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 387 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

Before After
Before After

View file

@ -12,7 +12,7 @@ license permits redistribution.
| `hermes.png` | [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) | `6ad632b` | MIT © 2025 Nous Research | `website/static/img/logo.png` | Cropped the baked-in border frame, padded to square, resized to 64×64, quantised to a 16-colour palette |
| `openclaw.svg` | [openclaw/openclaw](https://github.com/openclaw/openclaw) | `b06f40a` | MIT © 2026 OpenClaw Foundation | `ui/public/favicon.svg` | Removed the SMIL animation elements (renders the upstream rest pose statically — verified pixel-identical to the upstream frame at t=0); minified paths |
| `omp.svg` | [can1357/oh-my-pi](https://github.com/can1357/oh-my-pi) | `667111575ebba136dadfd6989379e7f67e0d40d9` | MIT © 2025 Mario Zechner; © 2025–2026 Can Bölük | `assets/icon.svg` | None |
| `kimi.png` | [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) | `4a550effdfcb29a25a5d325bf935296cc50cd417` | Apache-2.0; NOTICE: Kimi Code CLI © 2025 Moonshot AI | `web/public/logo.png` | None |
| `kimi.png` | [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) | `4a550effdfcb29a25a5d325bf935296cc50cd417` | Apache-2.0; NOTICE: Kimi Code CLI © 2025 Moonshot AI | `REMAPPING/meridian-web/public/logo.png` | None |
| `grok.svg` | [SpaceXAI brand guidelines](https://x.ai/legal/brand-guidelines) | Retrieved 2026-07-25 | xAI Brand Guidelines: marks may be used to accurately refer to xAI or its services; logos must be used exactly as provided | `SpaceXAI_Grok_Assets.zip` → `Grok_Logomark_Dark.svg` | None |
## Inline SVG marks (`RUNTIME_MARKS`)

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 965 B

After

Width:  |  Height:  |  Size: 965 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 795 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 878 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 612 B

After

Width:  |  Height:  |  Size: 612 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more