Files
narl-workspace/Cargo.toml
T
nvrl 1ae0665206 files: PDFs, text, code, tables and certificates open in place
The file pane decided what to show by MIME type, and uploads keep a
type only when it cannot execute, so a .md, a .json or a .pem arrived
as application/octet-stream and got "no preview". It now goes by name
first (preview.ts), and a file with a name it does not know has its
first 8 KiB looked at: no NUL bytes and valid UTF-8 means text.

- text and code: the first 256 KiB with line numbers, as before
- markdown: rendered through the same sanitiser as documents, with a
  source toggle
- CSV/TSV: a table of the first 500 rows, quotes respected
- PDF: the browser's own viewer in a frame. Only a PDF is served
  without the sandbox CSP (Chrome's viewer will not start under it),
  with frame-ancestors 'self' and X-Frame-Options SAMEORIGIN; the app
  CSP allows same-origin frames.
- certificates, keys, keystores: POST /api/nodes/{id}/inspect reads
  PEM (chains, keys, requests, public keys), DER and PKCS#12 on the
  server, in pure Rust, including the RC2/3DES keystores that
  openssl -legacy and older keychains write. It answers with subject,
  issuer, validity, key, names, usage and fingerprints, and pairs each
  private key with its certificate by public key. A password is asked
  for only when needed, used for that one answer and never stored;
  no key material is ever sent back. Sealed files are read through the
  vault, and only while it is open.

The tests read throwaway fixtures under src/routes/testdata, made with
the openssl commands listed beside them; .gitignore lets their .pem
files past a global *.pem rule.
2026-10-20 09:57:09 +02:00

91 lines
3.6 KiB
TOML

[package]
name = "narl-workspace"
version = "0.1.0"
edition = "2021"
# The Proton crates set the floor.
rust-version = "1.96"
description = "A terminal-themed personal diary for the browser"
[dependencies]
axum = { version = "0.8", features = ["multipart", "macros"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "fs", "io-util"] }
tower = "0.5"
tokio-util = { version = "0.7", features = ["io"] }
tower-http = { version = "0.6", features = ["trace", "compression-gzip", "limit", "set-header"] }
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite", "migrate", "macros"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
dotenvy = "0.15"
uuid = { version = "1", features = ["v4"] }
rand = "0.8"
hmac = "0.12"
sha2 = "0.10"
base64 = "0.22"
subtle = "2"
mime_guess = "2"
rust-embed = { version = "8", features = ["interpolate-folder-path"] }
regex = "1"
anyhow = "1"
zip = { version = "2", default-features = false, features = ["deflate"] }
tempfile = "3.27.0"
# Only for `--features keyring`, on a host that has a Secret Service; the
# container deployment has no session bus and falls back to the session file.
keyring = { version = "3", features = ["sync-secret-service", "crypto-rust"], optional = true }
# Mail. rustls rather than native-tls: the container has no OpenSSL, and the
# Proton client already pulls rustls in.
lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "pool", "builder", "hostname", "tokio1-rustls-tls"] }
# "did I write today?" and "is this due today?" are local-day questions, and DST
# rules out doing them with a fixed offset.
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
chrono-tz = "0.10"
# The vault: files encrypted at rest under a key only a password unlocks.
# AES-256-GCM in the STREAM construction, so a file of any size is sealed and
# opened a chunk at a time; Argon2id between the password and the key.
aes-gcm = { version = "0.10", features = ["stream"] }
argon2 = "0.5"
zeroize = { version = "1", features = ["serde"] }
futures-util = { version = "0.3", default-features = false, features = ["std"] }
proton-drive-rs = "0.9.2"
proton-sdk = "0.9.2"
rpassword = "7"
hex = "0.4"
# SFTP mount: spaces and folders as a filesystem, authenticated by pubkey
# instead of the cookie session. `ring` rather than the default `aws-lc-rs`
# backend, because rustls (via lettre) already pulls `ring` in.
russh = { version = "0.63", default-features = false, features = ["ring", "rsa"] }
russh-sftp = "3"
# Only for generating the host key at first boot — ssh-key needs an rng that
# speaks rand_core 0.10, one major version past the `rand` used everywhere else.
rand10 = { package = "rand", version = "0.10" }
# Reading certificates, keys and keystores for the file view, in pure Rust —
# there is no OpenSSL to lean on. p12-keystore brings RC2 and 3DES for the
# keystores older tools still export; pkcs8 opens encrypted PKCS#8 keys.
x509-parser = "0.18"
p12-keystore = "0.4"
pkcs8 = { version = "0.11", features = ["encryption", "3des", "std"] }
sec1 = { version = "0.8", features = ["der"] }
sha1 = "0.10"
[dev-dependencies]
# A paused clock, so tests of timed loops do not wait in real time.
tokio = { version = "1", features = ["test-util"] }
[features]
default = []
# Keep the Proton session in the OS keyring instead of a 0600 file. Needs a
# Secret Service on D-Bus, so it is off for the container deployment.
keyring = ["dep:keyring"]
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true