- Rust 95.6%
- Shell 3.3%
- Nix 1.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| crates | ||
| deploy/phoneserver | ||
| docs | ||
| scripts | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| clippy.toml | ||
| CORE.md | ||
| DESIGN-LOG.md | ||
| DESIGN.md | ||
| flake.lock | ||
| flake.nix | ||
| IMPL.md | ||
| NOTES.md | ||
| README.md | ||
chambers (working title)
A small, self-hosted file library with sharing between servers (earlier working title: Borges).
| Document | What |
|---|---|
| CORE.md | The core idea on one page: who owns what, how sharing works. Start here |
| DESIGN.md | The model and protocol: the current design, the single source of truth |
| IMPL.md | How to implement it (functional core, constructive types, testing) |
| DESIGN-LOG.md | Design decisions, newest last: what, why, and what led to them |
| NOTES.md | What implementing it taught us; the input to the log |
v0.1: experimental, expected to be thrown away. The core is meant to last; the server and UI are for exploring.
Layout
| Path | What |
|---|---|
crates/chambers-core |
The pure core (no_std): every type and decision of spec §6.0. No I/O. |
crates/chambers-core/tests/federation.rs |
Simulated federation: spec §14's scenarios across several in-process servers (tests/sim). |
crates/chambers-server |
The shell: axum HTTP, state in SQLite (<data>/chambers.db), blob store with outboards, outbound client (takes only core Requests), reconcile loop, outbox, moves (410, export/import as one SQLite file), and a plain-HTML UI. |
scripts/ |
dev.sh (N local servers), e2e.sh (real-HTTP scenarios), phone.sh (run on an Android emulator via adb). |
Core modules, in the order of the spec's model:
hash, file (files, the per-file limit), member (members: files and collection refs,
Members, Form), manifest (pages, roots, cut), authored (lists, published_root,
history), entry (Holding, Overrides, shown),
access (grants, Audience, capabilities, reach, may; read is the one right), sync (Follow, Mirror, Snapshot,
Walk, Mirrors, Policy, Transfer), identity (account key, records,
accept_record), links (actor documents, templates, routing, Request, with_token),
refs, notice (accept_notice), and account: the one value the shell stores, whose
transitions (create, edit_list, copy, delete, follow, set_mirror, keep, ...) keep
its invariants and whose derived reads (shown, current, member_of, visibility,
transfers, usage, ...) answer everything else. filing sits beside it, outside the model:
where collections go among the user's lists (holder, home, proposal, place, the default
lists), using only the account's public API.
Plus names, url, token, time, json (canonical encoding), protocol (the only place
the project name appears on the wire).
Develop
nix develop # or direnv
cargo test # core properties + simulated federation (~7 s)
nix flake check # tests, clippy -D warnings, fmt, and a real no_std build (wasm32v1-none)
Run
scripts/dev.sh 3 --fresh # http://127.0.0.1:8001, :8002, :8003 (dev mode)
scripts/e2e.sh # in another terminal: scenarios 2, 3, 4, 9 (move), 18 (lists of
# albums) and links over real HTTP; PORTS="8101 8102 8103" for others
A database from an earlier schema version isn't read (the server says so and stops); start with
--fresh or move dev-data away.
Open http://127.0.0.1:8001, register, upload, put a collection on your profile (public) or share it with
bob@127.0.0.1:8002; on the other server, accept it under "Incoming" or browse
alice@127.0.0.1:8001 under "Follow". Handles in dev mode include the port.
On an Android emulator
nix build .#static # result/bin/chambers: static aarch64 musl
scripts/phone.sh # builds that, pushes it, runs it on the device as 127.0.0.1:8004 (adb in PATH)
phone.sh bridges ports with adb reverse/adb forward, so the phone's server and the
host's servers reach each other as 127.0.0.1:<port>. Tested: a user on the emulator follows
a collection on the host (same root hash, kept copies), shares a file from the phone with a
user on the host, who plays it linked through his own server.
Production (not tried yet)
chambers --data DIR --listen 0.0.0.0:8080 --base https://host behind a TLS terminator.
Outbound requests then refuse loopback and private addresses (spec 13.2).
Registration is by invite unless --registration open (or closed: no new accounts). The first
account's invite code is in the log (no accounts yet: make the first one with invite code ...);
after that, accounts make codes on their Account page. A forgotten password is reset on the login
page with the account's recovery code.