Files
portal/Cargo.toml
T
Bendik LynghaugandClaude Opus 5 44c7ee6870
docs / deploy (push) Successful in 3s
Test / test (push) Successful in 2m9s
Publish release / publish (push) Successful in 1m52s
Render an Apple configuration profile from the server's PACC document
iOS is the one client none of the mail autoconfiguration reaches: Apple
Mail reads neither Mozilla autoconfig nor Autodiscover for IMAP, so
adding an account means the device probing for ports - and a probe that
meets a filtered port waits out a TCP timeout and reports the account as
unverifiable, which the person reads as a wrong password.

A profile states the hosts and the ports instead, and carries mail,
calendars and contacts in one install. It invents none of them: site.yaml
names a PACC document - the standard defined to be fetched from the mail
domain itself - and every host, port and collection path comes from
there, so the server is described in one place and the profile follows it.

The document holds no address, username or password. iOS asks for those
at install, which means one profile serves everyone with an account and
it can be fetched by anyone: it says no more than PACC already says in
public. Identifiers are derived from the site and the payload rather than
drawn at random, because iOS replaces a profile whose ids it knows and
stacks a duplicate when they differ.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o
2026-10-07 11:18:46 +02:00

160 lines
5.1 KiB
TOML

[package]
name = "portal"
version = "0.6.22"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
leptos = { version = "0.8" }
leptos_meta = { version = "0.8" }
leptos_router = { version = "0.8" }
serde = { version = "1", features = ["derive"] }
# preserve_order: a rendered page is read by the people who own it, so
# the keys come out as written (name, then description), not sorted.
serde_json = { version = "1", features = ["preserve_order"] }
# Inline markdown for content descriptions (links, emphasis, code) -
# runs on both server and client renders, so not feature-gated.
pulldown-cmark = { version = "0.13", default-features = false, features = ["html"] }
# --- server only ---
leptos_axum = { version = "0.8", optional = true }
axum = { version = "0.8", features = ["multipart"], optional = true }
aws-sdk-s3 = { version = "1", optional = true }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"], optional = true }
tower = { version = "0.5", optional = true }
tower-http = { version = "0.6", features = ["fs", "set-header"], optional = true }
tower-sessions = { version = "0.14", optional = true }
async-nats = { version = "0.38", optional = true }
arc-swap = { version = "1", optional = true }
url = { version = "2", optional = true }
serde_yaml = { version = "0.9", optional = true }
sha2 = { version = "0.10", optional = true }
openidconnect = { version = "4", optional = true }
# For pulling the `groups` custom claim out of the already-verified ID
# token's raw JWT payload - see server/oidc.rs::extract_groups_claim.
base64 = { version = "0.22", optional = true }
chrono = { version = "0.4", features = ["serde"], optional = true }
uuid = { version = "1", features = ["v4", "serde"], optional = true }
dotenvy = { version = "0.15", optional = true }
anyhow = { version = "1", optional = true }
futures = { version = "0.3", optional = true }
cedar-policy = { version = "4", optional = true }
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true }
# jq-filter evaluation for ResourceSource's optional `jq` field
# (src/resource.rs) - pure Rust, no shell-out to a `jq` binary.
jaq-core = { version = "3", optional = true }
jaq-std = { version = "3", optional = true }
jaq-json = { version = "2", features = ["sync"], optional = true }
# --- browser only ---
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
js-sys = { version = "0.3", optional = true }
console_error_panic_hook = { version = "0.1", optional = true }
web-sys = { version = "0.3", features = [
"Blob",
"File",
"FileList",
"FormData",
"HtmlInputElement",
"Navigator",
"Request",
"RequestInit",
"Response",
"Window",
], optional = true }
tower_governor = { version = "0.8.0", optional = true }
async-trait = { version = "0.1.92", optional = true }
[features]
default = []
hydrate = [
"leptos/hydrate",
"dep:wasm-bindgen",
"dep:wasm-bindgen-futures",
"dep:js-sys",
"dep:console_error_panic_hook",
"dep:web-sys",
]
ssr = [
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",
"dep:leptos_axum",
"dep:axum",
"dep:aws-sdk-s3",
"dep:tower_governor",
"dep:async-trait",
"dep:tokio",
"dep:tower",
"dep:tower-http",
"dep:tower-sessions",
"dep:async-nats",
"dep:arc-swap",
"dep:url",
"dep:serde_yaml",
"dep:sha2",
"dep:openidconnect",
"dep:base64",
"dep:chrono",
"dep:uuid",
"dep:dotenvy",
"dep:anyhow",
"dep:futures",
"dep:cedar-policy",
"dep:tracing",
"dep:tracing-subscriber",
"dep:jaq-core",
"dep:jaq-std",
"dep:jaq-json",
]
[dev-dependencies]
proptest = "1"
[[test]]
name = "jetstream"
required-features = ["ssr"]
[profile.wasm-release]
inherits = "release"
opt-level = 'z'
lto = true
codegen-units = 1
[package.metadata.leptos]
output-name = "portal"
# Disambiguates the app binary from the utility [[bin]] target
# (question_lint) added alongside it - cargo-leptos otherwise can't
# tell which of the two is "the" server.
bin-target = "portal"
site-root = "target/site"
site-pkg-dir = "pkg"
style-file = "style/main.css"
assets-dir = "public"
site-addr = "127.0.0.1:3010"
reload-port = 3011
bin-features = ["ssr"]
bin-default-features = false
lib-features = ["hydrate"]
lib-default-features = false
lib-profile-release = "wasm-release"
# Content-hashed pkg names (portal.<hash>.js/wasm/css) + hash.txt: a
# freshly served page can never reference a stale cached bundle - an
# iPhone kept a heuristically-cached portal.js across three releases
# and paired it with new wasm, silently killing hydration. Runtime
# needs LEPTOS_HASH_FILES=true (set in each content repo's deploy env).
hash-files = true
# cargo release <level> is how a portal version is cut: bump, commit,
# tag v{{version}}, push. CI (publish.yml) reacts to the tag and
# publishes the release artifact; nothing on crates.io.
[package.metadata.release]
publish = false
push = true
tag-name = "v{{version}}"
pre-release-commit-message = "Release {{version}}"