Skip to content

Commit 1b40dbf

Browse files
committed
chore: initial scaffold via mesh-common
0 parents  commit 1b40dbf

36 files changed

Lines changed: 5426 additions & 0 deletions

.githooks/commit-msg

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
msg_file="$1"
5+
first_line="$(sed -n '1p' "$msg_file")"
6+
7+
case "$first_line" in
8+
feat:\ *|fix:\ *|docs:\ *|chore:\ *|refactor:\ *|test:\ *|ops:\ *|data:\ *|build:\ *|style:\ *|perf:\ *|ci:\ *)
9+
exit 0
10+
;;
11+
*)
12+
printf '%s\n' "Commit message must use Conventional Commits, e.g. feat: add poll voting"
13+
exit 1
14+
;;
15+
esac

.githooks/pre-commit

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
npm run fmt:check
5+
npm run typecheck

.githooks/pre-push

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
npm run fmt:check
5+
npm run typecheck
6+
npm run smoke

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
node_modules/
2+
dist/
3+
.DS_Store
4+
*.log
5+
.env
6+
.env.local
7+
.env.*.local
8+
coverage/
9+
.vite/
10+
*.tsbuildinfo
11+
test-results/
12+
test-results.json
13+
playwright-report/
14+
.playwright/

.prettierignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
docs/assets
3+
docs/index.html
4+
docs/404.html
5+
package-lock.json

.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"semi": true,
3+
"singleQuote": false,
4+
"trailingComma": "all",
5+
"printWidth": 100
6+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Florin Badita
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# mesh-caption-clash
2+
3+
[![pages](https://img.shields.io/badge/live-baditaflorin.github.io%2Fmesh-caption-clash-db2777)](https://baditaflorin.github.io/mesh-caption-clash/)
4+
[![version](https://img.shields.io/badge/version-0.1.0-blue)](https://github.com/baditaflorin/mesh-caption-clash/blob/main/package.json)
5+
[![license](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
6+
7+
> A shared caption contest with one entry and independent votes per peer.
8+
9+
**Live → https://baditaflorin.github.io/mesh-caption-clash/**
10+
11+
**Source → https://github.com/baditaflorin/mesh-caption-clash**
12+
13+
**Tip the dev (buy a coffee) → https://www.paypal.com/paypalme/florinbadita**
14+
15+
---
16+
17+
![screenshot](docs/screenshot.png)
18+
19+
> Two peers, side-by-side, in the same room. Drop a `tests/demo/scenario.mjs`
20+
> exporting `default async (a, b) => …` and run `npm run demo` to regenerate
21+
> `docs/preview.png` plus `docs/demo-a.webm` / `docs/demo-b.webm` clips.
22+
23+
![preview](docs/preview.png)
24+
25+
## What it is
26+
27+
A **rootless-computing** peer-to-peer browser app. No backend of its own beyond the self-hosted WebRTC stack listed below. State lives in a Yjs mesh shared by everyone in the same room.
28+
29+
Read the principles → **https://baditaflorin.github.io/rootless-computing/principles.html**
30+
31+
## Quickstart
32+
33+
Open the live URL on two devices in the same room (set in ⚙ settings, or scan the room QR). Everything else is in-app.
34+
35+
For local hacking:
36+
37+
```bash
38+
git clone https://github.com/baditaflorin/mesh-common
39+
git clone https://github.com/baditaflorin/mesh-caption-clash
40+
cd mesh-caption-clash
41+
npm install
42+
npm run dev
43+
```
44+
45+
`mesh-common` must sit as a **sibling** directory because `package.json` references it via `file:../mesh-common`.
46+
47+
## Self-hosted infrastructure
48+
49+
| Repo | Endpoint | Purpose |
50+
| ------------------------------------------------- | -------------------------------------- | --------------------------- |
51+
| https://github.com/baditaflorin/signaling-server | `wss://turn.0docker.com/ws` | y-webrtc signaling fan-out |
52+
| https://github.com/baditaflorin/turn-token-server | `https://turn.0docker.com/credentials` | HMAC TURN creds, 1-hour TTL |
53+
| https://github.com/baditaflorin/coturn-hetzner | `turn:turn.0docker.com:3479` | TURN relay |
54+
55+
## Settings overrides
56+
57+
The settings drawer lets the user override signaling and TURN endpoints. localStorage keys:
58+
59+
- `mesh-caption-clash:signalingUrl`
60+
- `mesh-caption-clash:turnTokenUrl`
61+
- `mesh-caption-clash:iceServers`
62+
- `mesh-caption-clash:room`
63+
64+
If endpoints are blank or unreachable, the app falls back to STUN-only.
65+
66+
## Version + commit on every screen
67+
68+
The bottom-right footer on every screen of the live app shows:
69+
70+
- `source` → this repo
71+
- `tip ♥` → PayPal
72+
- `vX.Y.Z · <short-sha>` — version from `package.json` plus the build-time git commit
73+
74+
## Build & deploy
75+
76+
GitHub Pages serves the committed `docs/` directory on the `main` branch. There is no GitHub Actions build workflow; local Husky-style hooks gate formatting / typecheck / smoke build before each push.
77+
78+
```bash
79+
npm run smoke # build + sanity-check docs/
80+
bash ../mesh-common/scripts/screenshot-app.sh # regenerate docs/screenshot.png
81+
```
82+
83+
## Privacy
84+
85+
<!-- mesh:privacy-section:start -->
86+
87+
Everything you publish to a room is visible to every peer in that room. Your local device's name, key, and choices stay local. Cryptographic signatures prove **who** wrote each entry; they do **not** prevent peers from reading or copying entries. The room URL is the access control — share it deliberately.
88+
89+
See `docs/privacy.md` for the full threat model — capabilities used, what other peers in the mesh see, what the self-hosted infra sees, what stays local.
90+
<!-- mesh:privacy-section:end -->
91+
92+
## License
93+
94+
MIT — see `LICENSE`.

docs/adr/0001-deployment-mode.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# 0001 — Deployment mode: Mode A (Pure GitHub Pages)
2+
3+
- **Status**: accepted
4+
- **Date**: 2026-05-13
5+
6+
## Context
7+
8+
This app is a peer-to-peer mesh; the only "backend" needed is signaling + TURN, both already self-hosted (see README). All app state lives in browsers via Yjs CRDT. No auth, no DB.
9+
10+
## Decision
11+
12+
Ship as **Mode A** — pure client-side GitHub Pages app served from `docs/`.
13+
14+
- Build output committed to `docs/`, not produced by a GitHub Actions workflow (the account-wide Actions billing lock makes A the only feasible mode anyway).
15+
- All runtime dependencies (signaling, TURN) are overridable via the in-app settings drawer.
16+
17+
## Consequences
18+
19+
- ✅ Zero runtime cost, no servers to operate beyond the shared WebRTC stack.
20+
- ✅ Reproducible: `docs/` is checked in and `git log -- docs/` shows the deploy history.
21+
- ❌ No server-side validation; assume any peer can send arbitrary CRDT updates.
22+
- ❌ No persistence beyond connected peers (acceptable — this is by design).
23+
24+
## Alternatives considered
25+
26+
- **Mode B (Pages + pre-built data)**: rejected — there is no offline data to bake in.
27+
- **Mode C (Docker backend)**: rejected — no functionality requires authoritative state. The self-hosted signaling/TURN already exists as shared infra across all mesh-* apps.

docs/adr/0010-pages-publishing.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# 0010 — GitHub Pages publishing strategy
2+
3+
- **Status**: accepted
4+
- **Date**: 2026-05-13
5+
6+
## Context
7+
8+
GitHub Pages will serve the live app. There are three publishing strategies: `main / (root)`, `main / docs`, or a `gh-pages` branch.
9+
10+
## Decision
11+
12+
Publish from **`main` branch, `/docs` folder**.
13+
14+
- Vite is configured with `base: "/mesh-caption-clash/"` and `build.outDir: "docs"`.
15+
- `docs/index.html` is copied to `docs/404.html` as the SPA fallback (GitHub Pages doesn't support `_redirects`).
16+
- `docs/` is **not** gitignored — the committed build output is the deploy.
17+
- No GitHub Actions; Husky pre-push hook runs `npm run smoke` which rebuilds `docs/`.
18+
19+
## Consequences
20+
21+
- ✅ Zero CI cost; account-wide Actions billing lock is irrelevant.
22+
- ✅ Every commit's deployed code is auditable in git history.
23+
- ❌ Committed `dist`-style output bloats git history; mitigated by keeping the bundle small.
24+
- ❌ Authors must remember to run `npm run smoke` before push (enforced by hook).

0 commit comments

Comments
 (0)