| frontend | ||
| migrations | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| DESIGN.md | ||
| mapserver.service | ||
| nginx-snippet.conf | ||
| postgres.container | ||
| README.md | ||
| rust_push.sh | ||
Waymark — build and deploy
Single Rust binary + one rootless Postgres container. See DESIGN.md for the reasoning.
This code has not been compiled. It was written without a Rust toolchain available, so treat the first
cargo buildas the real review. Expect to fix a handful of import paths and trait-bound details, not the structure.
1. Refresh dependency versions
Cargo.toml has plausible versions, but pin them properly:
cargo add axum -F multipart,macros
cargo add tokio -F full
cargo add sqlx --no-default-features -F runtime-tokio,tls-rustls,postgres,uuid,time,macros,migrate
cargo add tower-sessions
cargo add tower-sessions-sqlx-store -F postgres
cargo add lettre --no-default-features -F tokio1-rustls,smtp-transport,builder,pool
cargo add rust-embed -F debug-embed
cargo build
Two version-sensitive spots to check first if it doesn't compile:
tower-sessions/tower-sessions-sqlx-storemust be a matching pair. The store crate tracks the main crate's version and they break in lockstep. Check the store's own README for whichtower-sessionsit expects.- axum 0.8 changed path params from
:idto{id}. This code uses{id}. If you end up on 0.7 for some reason, they all need changing back.
2. Database
podman secret create mapserver-db-password - # type the password, then Ctrl-D
mkdir -p ~/.config/containers/systemd
cp deploy/postgres.container ~/.config/containers/systemd/
systemctl --user daemon-reload
systemctl --user start postgres
Migrations run automatically on startup — both ./migrations and the session store's own.
3. Config
mkdir -p ~/.config/mapserver ~/gpx
cp .env.example ~/.config/mapserver/.env
chmod 600 ~/.config/mapserver/.env
$EDITOR ~/.config/mapserver/.env
Must change: DATABASE_URL, REG_TOKEN, PUBLIC_URL, BASE_PATH, GPX_DIR.
Leave SMTP_HOST empty for now — reset links get logged instead of emailed, and the whole flow
is testable without a provider.
4. Run
cargo build --release
mkdir -p ~/.local/bin && cp target/release/mapserver ~/.local/bin/
cp deploy/mapserver.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now mapserver
loginctl enable-linger "$USER" # or nothing starts at boot
journalctl --user -u mapserver -f
5. nginx
Merge deploy/nginx-snippet.conf into your existing TLS server block, then
nginx -t && systemctl reload nginx.
6. First account
Visit https://your-domain/maps/, choose "Create an account", and enter the REG_TOKEN from
.env.
Testing the reset flow with real mail
mailpit gives you a local SMTP server and a web inbox with no signup:
SMTP_HOST=127.0.0.1
SMTP_PORT=1025
SMTP_TLS=none
SMTP_USERNAME=
SMTP_PASSWORD=
When you move to a real provider: use an app-specific password (any account with 2FA will reject
the normal one over SMTP), check SMTP isn't disabled by default on the account, and make sure
SMTP_FROM is an address that account may send as.
Things worth checking by hand after the first deploy
curl -i https://your-domain/maps/api/gpx/file/../../etc/passwd→ 400 or 404, never a file- Live location works (needs HTTPS; it silently fails on plain HTTP)
- Session survives
systemctl --user restart mapserver - Upload a >2 MB GPX — catches both the axum and nginx body limits at once
- Upload the same filename twice — second one should come back as
name-1.gpx - Reboot the server, confirm both units come back (this is what
enable-lingeris for)
Unit tests
cargo test
No database needed — every test is pure: base-path normalisation, bbox parsing, GPX filename safety, and the rate-limiter window.