Files
runway/Cargo.toml
T
connor c7e22f4431 Add authentication
Proving who somebody is and starting a session for them are separate
operations. login_with_caldav does the first by asking the CalDAV server
whether the credentials work; begin_session does the second and knows
nothing about how the question was answered. OIDC arrives as a second way
to reach begin_session, not as a second scheme threaded through everything
-- which is what v1 had, with a JWT for most of the app and a separate
SQLite session_token used only by the preferences API.

The token lives in an HttpOnly cookie and nowhere else. v1 kept a JWT and
the CalDAV password in localStorage, readable by any script on the origin,
and re-sent the password in a header on every request. Here the password
never leaves the server: it is encrypted with XChaCha20-Poly1305 and
caldav_for is the only path back, handing out a client rather than a
credential.

Failed decryption is an error, not a subtly wrong password -- the AEAD tag
is checked, so a tampered row surfaces here instead of as a mysterious
CalDAV rejection later. A wrong password and an unreachable server stay
distinct, because telling somebody their password is wrong when the server
is down sends them to reset one that was fine.

Errors carry a stable code alongside their message, so a client can branch
on them. Internal ones say nothing about the inside of the server; the
detail goes to the log.

Tests go through router(), the same function main calls -- v1's suite
rebuilt the route table and tested a copy until it stopped compiling.
Skipping is now loud: a skipped test reports "ok", so run.sh sets
RUNWAY_REQUIRE_CALDAV=1 and not running becomes a failure.
2026-08-26 16:27:59 -04:00

80 lines
1.8 KiB
TOML

[workspace]
resolver = "3"
members = [
"crates/runway-core",
"crates/runway-caldav",
"crates/runway-server",
"crates/runway-web",
"crates/runway-cli",
]
[workspace.package]
version = "0.1.0"
edition = "2024"
license = "MIT"
repository = "https://github.com/connorjohnstone/runway"
[workspace.lints.rust]
dead_code = "deny"
unused_must_use = "deny"
unsafe_code = "forbid"
[workspace.lints.clippy]
# v1 had 65 of these. Tests opt out locally.
unwrap_used = "deny"
expect_used = "deny"
[workspace.dependencies]
# Internal
runway-core = { path = "crates/runway-core" }
runway-caldav = { path = "crates/runway-caldav" }
# Core data
chrono = { version = "0.4", default-features = false, features = ["serde", "clock", "std"] }
chrono-tz = { version = "0.10", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4", "serde"] }
# Calendar domain
icalendar = "0.17"
rrule = "0.14"
# Crypto
sha2 = "0.10"
chacha20poly1305 = "0.10"
rand = "0.9"
base64 = "0.22"
# Errors + logging
thiserror = "2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Backend
axum = "0.8"
axum-extra = { version = "0.10", features = ["cookie"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "signal"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "trace"] }
sqlx = { version = "0.9", features = ["runtime-tokio", "sqlite", "chrono", "uuid", "migrate"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
quick-xml = "0.42"
# Frontend
leptos = "0.8"
leptos_router = "0.8"
# CLI
clap = { version = "4", features = ["derive", "env"] }
iana-time-zone = "0.1"
# Dev
pretty_assertions = "1"
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = true