Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
aa15a1b
fix(rrc): split room history to remove ineffective dynamic import
rinchen Jul 29, 2026
a57a301
fix(reticulum): fix rncp request-enable 422 and auto-share receive dest
rinchen Jul 29, 2026
b4dcc19
feat(app): add setting to force 24-hour timestamps
rinchen Jul 29, 2026
b47c585
fix(reticulum): stop link-timeout bridge from locking PN ✗ after Dire…
rinchen Jul 29, 2026
eb3259f
fix(rrc): soften involuntary part banner and log hub PART events
rinchen Jul 29, 2026
1cc8ba4
feat(reticulum): host local LXMF PN with peering policy controls
rinchen Jul 29, 2026
dceaf1e
fix(reticulum): make peer People avatar selectable and visible
rinchen Jul 29, 2026
a061851
fix(rrc): ignore other-member PARTED fanout as self-leave
rinchen Jul 29, 2026
387cb5b
fix(reticulum): overlay discovery announce egress for RMAP
rinchen Jul 29, 2026
2a5f3d8
fix(reticulum): apply discovery announce egress overlay once
rinchen Jul 29, 2026
1a6a36f
fix(reticulum): overlay PropagationNode policy setters for rns-stack CI
rinchen Jul 29, 2026
b2adacc
chore(reticulum): track rsLXMF policy-setters overlay against PR #6
rinchen Jul 29, 2026
66d5bdb
fix: harden propagation sync state and CI from audit backlog
rinchen Jul 30, 2026
cd24e10
chore: sync package.json contributors from docs/credits.md
rinchen Jul 30, 2026
bbd4a86
fix: harden audit remediations for PN hosting and RNCP share consent
rinchen Jul 30, 2026
659fa4d
fix(reticulum): reuse failed LXMF chat row on manual Retry
rinchen Jul 30, 2026
c16f834
fix(reticulum): deliver PN deposits and keep Completes green on Retry
rinchen Jul 30, 2026
7f982cc
fix(reticulum): overlay has_pending_to and clear Sonar PR findings
rinchen Jul 30, 2026
d68c400
fix(reticulum): harden PN hosting, audit gate, and clippy CI
rinchen Jul 30, 2026
81330ca
fix(reticulum): clear Sonar smells and tighten hardening contracts
rinchen Jul 30, 2026
a980582
refactor(reticulum): move LXMF send helpers out of useSendMessage
rinchen Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

[*.rs]
indent_size = 4
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto eol=lf

*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.icns binary
*.woff binary
*.woff2 binary
*.wasm binary
6 changes: 6 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ else
printf 'pre-commit: skip check:reticulum-interface-modes (no interface-mode paths staged)\n' >&2
fi

if staged_match '^(reticulum-sidecar/src/stack/pn_hosting_policy\.rs|src/shared/pnHostingPolicy\.ts|scripts/check-pn-hosting-policy\.mjs)'; then
pnpm run check:pn-hosting-policy || fail 'pnpm run check:pn-hosting-policy'
else
printf 'pre-commit: skip check:pn-hosting-policy (no pn-hosting-policy paths staged)\n' >&2
fi

if staged_match '^(reticulum-sidecar/src/stack/config\.rs|src/shared/reticulumDecommissionedHubs\.ts|scripts/check-reticulum-decommissioned-hubs\.mjs)'; then
pnpm run check:reticulum-decommissioned-hubs || fail 'pnpm run check:reticulum-decommissioned-hubs'
else
Expand Down
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
version: 2
updates:
# Dependabot PRs intentionally disabled (limit 0) — the project manages npm
# and Actions updates via `pnpm run update` (scripts/update.sh) which runs
# dedupe, patch checks, and Ratspeak/rsReticulum verification in one pass.
# See AGENTS.md §6 and docs/ci-cd.md.
- package-ecosystem: 'npm'
directory: '/'
schedule:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,29 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Format check
run: pnpm run format:check

- name: Markdown lint
run: pnpm run lint:md

- name: Lint
run: pnpm run lint

- name: License check
run: pnpm run check:licenses

- name: Install actionlint
run: pnpm run setup:actionlint

- name: Actionlint
run: |
export PATH="${GITHUB_WORKSPACE}/.githooks/bin:${PATH}"
actionlint

- name: pnpm audit (high+)
run: pnpm audit --audit-level=high

- name: Install yamllint
run: pip install yamllint

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/reticulum-sidecar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ on:
- 'scripts/build-reticulum-sidecar-release.mjs'
- 'scripts/clone-ratspeak-stack.sh'
- 'scripts/ensure-rsReticulum-patches.sh'
- 'scripts/apply-rsLXMF-propagation-sync-peering.sh'
- 'scripts/apply-rsLXMF-*.sh'
pull_request:
paths:
- 'reticulum-sidecar/**'
- '.github/workflows/reticulum-sidecar.yaml'
- 'scripts/build-reticulum-sidecar-release.mjs'
- 'scripts/clone-ratspeak-stack.sh'
- 'scripts/ensure-rsReticulum-patches.sh'
- 'scripts/apply-rsLXMF-propagation-sync-peering.sh'
- 'scripts/apply-rsLXMF-*.sh'

jobs:
lint:
Expand Down
5 changes: 3 additions & 2 deletions .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
"ignores": [
"node_modules/**",
".cursor/**",
".hermes/**",
"site/**",
"release/**",
"dist/**",
"out/**"
"out/**",
],
}
}
4 changes: 3 additions & 1 deletion .markdownlint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"MD001": false,
"MD013": false,
"MD022": false,
"MD024": false,
"MD024": {
"siblings_only": true
},
"MD025": false,
"MD031": false,
"MD032": false,
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ tmp/

# Files unsupported by prettier parser
.npmrc
.editorconfig
.gitattributes
patches/
*.plist
*.txt
Expand Down
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"davidanson.vscode-markdownlint",
"rust-lang.rust-analyzer"
]
}
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact"],
"eslint.format.enable": true,
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
Expand All @@ -19,6 +19,6 @@
"editor.defaultFormatter": "vscode.json-language-features"
},
"[typescriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
11 changes: 7 additions & 4 deletions AGENTS.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/accessibility-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ This is a living document. Check items against VoiceOver (macOS), NVDA (Windows)
- [ ] **Setting on — still allowed (essential feedback)**: Loading spinners (`animate-spin`, `Loader`), connection header status pulses (`animate-pulse` on MQTT/device labels and status dots).
- [ ] Toggle persists across restart (SQLite + localStorage reconcile on mount, same pattern as `locale` / `chatCompactMode`).
- [x] Optional first-run: if `reduceMotion` key is absent, initializer may default from `matchMedia('(prefers-reduced-motion: reduce)')` once; thereafter only the App toggle applies (not live-synced to OS changes). Implemented by `initReduceMotionDefaultIfAbsent()` in `reduceMotionPreference.ts` (called from `main.tsx` before React mount).
- [ ] **24-hour time (in-app setting)**: **App → Appearance → Use 24-hour time** (`use24HourTime` via `timeFormatStore` / `formatDisplayTime`; SQLite + localStorage, same bundle as Reduce motion). When on, chat and other display clocks force 24-hour format; when off, follow the system locale.

---

Expand Down
25 changes: 16 additions & 9 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,24 @@ Mesh-Client uses GitHub Actions for continuous integration and deployment.

## CI Build (`ci.yaml`)

Runs on every push and pull request to `main`:
Runs on every push and pull request to `main` (and `workflow_dispatch`):

1. Checkout code
2. Setup pnpm
3. Setup Node 22
4. Install dependencies (`pnpm install --frozen-lockfile`)
5. Run lint (`pnpm run lint`)
6. Run typecheck (`pnpm run typecheck`)
7. Run build (`pnpm run build`)
8. Run `yamllint` on workflow/config YAML
9. Run `check:flatpak`, `check:flatpak-offline-pnpm` (needs `flatpak-node-generator`), `desktop-file-validate`, and `appstreamcli validate` on Flatpak metadata

All steps must pass before a PR can be merged.
5. Format check (`pnpm run format:check`)
6. Markdown lint (`pnpm run lint:md`)
7. Run lint (`pnpm run lint`)
8. License check (`pnpm run check:licenses`)
9. actionlint (via `pnpm run setup:actionlint`)
10. `pnpm audit --audit-level=high` (non-blocking warning)
11. Run `yamllint` on workflow/config YAML
12. Run typecheck (`pnpm run typecheck`)
13. Run build (`pnpm run build`)
14. Run `check:flatpak`, `check:flatpak-offline-pnpm` (needs `flatpak-node-generator`), `desktop-file-validate`, and `appstreamcli validate` on Flatpak metadata

All blocking steps must pass before a PR can be merged.

---

Expand Down Expand Up @@ -119,7 +124,9 @@ Automated dependency updates are configured in `.github/dependabot.yml`:
- **Schedule:** Weekly on Saturdays
- **npm dependencies:** Grouped PRs (Electron separate, all other deps together)
- **GitHub Actions:** Grouped into one PR
- **Limit:** 10 open PRs maximum
- **Open PRs:** `open-pull-requests-limit: 0` — Dependabot scans but does **not** open PRs.
Dependency bumps are applied manually via `pnpm run update` (`scripts/update.sh`), which
also runs dedupe and Ratspeak/rsReticulum patch checks. See AGENTS.md §6.

### Testing Dependabot PRs locally

Expand Down
36 changes: 31 additions & 5 deletions docs/reticulum-sidecar-ipc.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,42 @@ The Connection tab UI edits a subset: **name** and **mode** for all types; **hos
**WS `rmap.discovery`:** sidecar polls DiscoveryStore every **10s**; emits full `{ discovered: [...] }` snapshot when JSON fingerprint changes. Stub builds return `{ discovered: [] }`.
| GET | `/api/v1/packets` | `?limit=500` (1–2500) | `{ packets: [] }` — recent wire tap ring buffer |
| DELETE | `/api/v1/packets` | | `{ ok }` — clear wire tap buffer |
| GET | `/api/v1/propagation` | | `{ propagation, preferred_id, auto_sync_interval_sec }` — `local-prop` rows include `message_count`, `storage_bytes` when live |
| GET | `/api/v1/propagation` | | `{ propagation, preferred_id, auto_sync_interval_sec, pn_hosting_policy }` — `local-prop` rows include `message_count`, `storage_bytes` when live |
| GET | `/api/v1/propagation/discovered` | | `{ discovered: DiscoveredPropagationRow[] }` — heard `lxmf.propagation` announces (not auto-configured) |
| POST | `/api/v1/propagation/add` | `{ destination_hash, name? }` | `{ ok, node }` — add a remote propagation node by hash |
| POST | `/api/v1/propagation/add` | `{ destination_hash, name?, skip_probe? }` | `{ ok, node }` or `{ ok: false, error }` — probes `/offer` unless `skip_probe`; may return `PROPAGATION_OFFER_UNSUPPORTED`, `PROPAGATION_PEER_COST_EXCEEDS_MAX`, identity/path errors |
| POST | `/api/v1/propagation/hosting-policy` | `PnHostingPolicy` | `{ ok }` — persist + apply local PN hosting / peering policy |
| PUT | `/api/v1/propagation/{id}` | `{ name }` | `{ ok }` — rename a remote node (`local-prop` rejected) |
| DELETE | `/api/v1/propagation/{id}` | | `{ ok }` — remove a remote node (`local-prop` rejected; clears preferred if that id) |
| POST | `/api/v1/propagation/{id}/enable` | | `{ ok }` |
| POST | `/api/v1/propagation/{id}/disable` | | `{ ok }` |
| POST | `/api/v1/propagation/{id}/enable` | | `{ ok }` — for `local-prop`, starts PN serve + announce |
| POST | `/api/v1/propagation/{id}/disable` | | `{ ok }` — for `local-prop`, stops PN serve + announce |
| POST | `/api/v1/propagation/{id}/preferred` | | `{ ok }` |
| POST | `/api/v1/propagation/sync` | | `{ ok }` |
| POST | `/api/v1/propagation/sync/cancel` | | `{ ok }` |
| POST | `/api/v1/propagation/auto-sync-interval` | `{ interval_sec }` | `{ ok }` — `0` disables periodic sync; persists with stack |

**`PnHostingPolicy`** (mirrored in `src/shared/pnHostingPolicy.ts` / sidecar `pn_hosting_policy.rs`):

| Field | Default | Notes |
| -------------------------- | ------- | ----------------------------------------------- |
| `peering_cost` | `18` | Must be ≤ `max_peering_cost` |
| `max_peering_cost` | `26` | |
| `autopeer` | `true` | |
| `autopeer_maxdepth` | `4` | Cap 64 |
| `max_peers` | `20` | 1–256 |
| `propagation_stamp_cost` | `16` | |
| `propagation_stamp_flex` | `3` | Must be ≤ stamp cost |
| `message_storage_limit_mb` | `256` | 1–10240 |
| `propagation_limit_kb` | `256` | 1–102400 |
| `sync_limit_kb` | `10240` | 1–102400 |
| `delivery_limit_kb` | `1000` | 1–102400 |
| `from_static_only` | `false` | |
| `auth_required` | `false` | |
| `enforce_stamps` | `false` | |
| `enforce_ratchets` | `false` | |
| `static_peers` | `[]` | Lowercase 32-hex hashes (max 256) |
| `node_name` | `null` | Trimmed; max 128 scalar chars; no control chars |
| `pn_announce_interval_sec` | `360` | Cap 86400 |
| `announce_at_start` | `true` | |

### Nomad Network

Expand Down Expand Up @@ -226,7 +252,7 @@ Renderer calls `electronAPI.reticulum.*`; main process proxies to this API (sand

`getStatus` / `onStatus` may include `interfaceIssueAlert` (TCP connect failures, TX queue drops, link-delivery timeouts, transport saturation / slow queries, **`bleBondRemoved`** stale RNode bonds, **`blePairingTimedOut`** OS passkey / TX-read timeouts). Per-entry latch timestamps use a **5-minute** stale window (`RETICULUM_INTERFACE_ISSUE_ALERT_STALE_MS`). Connection syncs **enabled** interface names via `syncInterfaceIssueScope` so disabling or removing an interface clears that name immediately and rejects re-latch from lagging log lines. Stopping the stack (or unexpected process exit) clears the tracker.

**`propagation_sync` WebSocket payload:** `{ active: boolean, progress: number, message: string | null }`. Progress uses 0–100 (Establishing ≈10, Offering ≈25, …, Complete ≈100). Sticky success after HaveAll emits `active:false, progress:100`; cancel/stall/failure emit `active:false, progress:0` (and must not emit a trailing 100). Sync `POST /api/v1/propagation/sync` may return `PROPAGATION_IDENTITY_UNKNOWN`, `PROPAGATION_TARGET_NOT_PN`, `PROPAGATION_PEERING_STAMP_FAILED`, or `LOCAL_PROPAGATION_SYNC_UNSUPPORTED`.
**`propagation_sync` WebSocket payload:** `{ active: boolean, progress: number, message: string | null }`. Progress uses 0–100 (Establishing ≈10, Offering ≈25, …, Complete ≈100). Sticky success after HaveAll emits `active:false, progress:100`; cancel/stall/failure emit `active:false, progress:0` (and must not emit a trailing 100). Sync `POST /api/v1/propagation/sync` may return `PROPAGATION_IDENTITY_UNKNOWN`, `PROPAGATION_TARGET_NOT_PN`, `PROPAGATION_PEERING_STAMP_FAILED`, `PROPAGATION_PEER_COST_EXCEEDS_MAX`, or `LOCAL_PROPAGATION_SYNC_UNSUPPORTED`. Add may return `PROPAGATION_OFFER_UNSUPPORTED` / probe timeout failures.

SQLite chat history uses separate `db:*` handlers (`getReticulumMessages`, `saveReticulumMessage`, `searchReticulumMessages`, `deleteReticulumMessage`, destination upserts), not sidecar HTTP. Remote saved addresses / inbound policy and RRC room history also use dedicated `db:*` handlers (not sidecar HTTP).

Expand Down
Loading