v0.23.0 — one app builder, typed secrets
The whole app surface now goes through a single builder, and every secret in the
stdlib is a typed, self-redacting value instead of a bare String. Two large
workstreams — one of them breaking.
Std.App — one builder, one --target
Describe an app once — App.app { init, update, view, subscriptions } — and a
build-time --target family[:variant] picks the backend; App.run is the single
entry. App.app takes a Std.Ui view that renders across web, terminal, and
desktop; App.web takes a Std.Html view; and new App.cli / App.tui
take a hand-authored String view for the terminal (the first-class successors
to Std.Cli.program / Std.Tui.program). Config layers in two typed pieces —
withBase (cross-target log / database / telemetry) and withConfig (a
per-target variant whose name matches the --target family). A String-view app
pins its backend with [app] target = "terminal:cli" in sky.toml.
The five per-shape front doors — Std.Live, Std.Spa, Std.Tui, Std.Cli,
Std.Webview — are now deprecated for direct use (they still compile; sky doc groups them under "deprecated — use Std.App"). Nothing in user code needs to
import them directly any more.
⚠ Breaking — secrets are a typed Sky.Core.Secret, not String
Every secret-bearing argument in the stdlib is now the opaque Sky.Core.Secret,
which redacts itself ([REDACTED]) in every log / print / JSON / %v path. This
is a signature change — existing code that passes a String will not type-check
until it is wrapped:
| API | before | after |
|---|---|---|
Auth.signToken / verifyToken / signSlidingToken |
String key |
Secret key |
Jwt.hs256 |
String |
Secret |
Jwt.rs256 |
one String key |
private Secret; verify moved to rs256Verify : String |
Crypto.aesGcmEncrypt/Decrypt, chacha20*, aesKeyFromPassword |
String key |
Secret |
Http.withBearer / withApiKey |
String |
Secret |
Cli.readPassword |
returned String |
returns Secret |
Migration: wrap at the boundary — Secret.fromEnv "MY_VAR" (recommended) or
Secret.fromString runtimeString — and unwrap only through the greppable
Secret.reveal. A committed string literal to any of these is now a deliberate
compile error (secrets must not be committed to source). DB connection passwords
are also redacted in connect-error logs. Full guide:
docs/security/secret-migration.md.
Soundness
- Fixed a cross-module type-collision miscompile. A local type whose
unqualified name shadowed a kernel type — e.g. a usertype Routenext to
Std.Live.Route— could bind the foreign kernel handle to the local nominal
and panic at runtime (rt.Coerce) whilesky checkpassed.Std.Live.Route
is now a declared opaque type (matchingStd.Spa/Sky.Http.Server), and
codegen no longer resolves a bare kernel-implicit name to a same-named local —
closing the class for every kernel-implicit name (Session,Request,
Response, …), not justRoute. - New
xtask erasure-fuzzgate. It generates well-typed erasure-crossing
programs and builds and runs them, assertingtype-check ⟹ go build ⟹ no panic— a permanent guard for the "compiles clean, panics at runtime" class.
Type checking & diagnostics
- Unknown qualified kernel members are rejected at
sky check, not codegen.
Calling a member a kernel module does not have —List.sum,
Basics.remainderBy, or an unknown member of a.sky-migrated module such as
Live.nope/Jobs.nope/Tui.nope— now fails at type-check with a clear
[E1001]"no member" error and a did-you-mean, instead of type-checking to
anyand failing later with a codegen[E4005]. A newxtask kernel-members
drift gate keeps the compiler's kernel tables, the stdlib.skyexposing
lists, and the runtime exports in sync — with a proven falsifier — so the class
cannot silently return. (sortWith/sortBy/filterMapare now importable
both qualified and viaexposing; the phantomList.parallelMap/
Io.readBytesare gone.) - Compiler hints link to public docs. Diagnostics that reference a guide —
the typed-Secretmigration hint above, the[observability]/sky.toml
build notes, the config-migration notice, and thesky docdeprecation notes
on the retired front-door modules — now point at a full
https://github.com/anzellai/sky/blob/main/docs/…URL instead of a
repo-relativedocs/…path that asky upgradeuser has no way to open.
Reliability
sky installno longer fails on a transient inspector-spawn race. The Go
FFI inspector (sky-ffi-inspect) is retried onETXTBSY("text file busy") —
the case where a just-built inspector binary is still held open by the linker
at the instant it is executed — so a first inspection after a rebuild no longer
fails spuriously.