Mako treats safety as a compiler and runtime contract, not a style guide. The goal: make leaks, memory corruption, and common backend footguns hard to ship — by construction where possible, by hard errors where not.
Guided tour: The Mako Book §11.
[package] systems = true forbids GC weakening of
hold/share/move rules.| Risk | Mako prevention |
|---|---|
| Leaks (scoped work) | Values and arena regions are released at scope end |
| Orphan threads | crew cancel_joins all kicked jobs on exit |
| Buffer overflow | Array/string index bounds-checked in debug; unsafe / unsafe_index opt-out |
| Use-after-move | CFG NLL + hold move checker (use of moved value) |
| Secrets in memory | secret_from_str / secret_drop — wipe via mako_secure_zero |
| Raw pointer games | Not available in safe Mako |
hold bindings move on rebind, into calls, and on full reads of non-Copy types.
Use-after-move is a hard type error with a clear hint. See
examples/bad/hold_use_after_move.mko and GUIDE § ownership.
let tok = secret_from_str(api_key)
// ... use ...
secret_drop(tok) // explicit_bzero / memset_s / volatile wipe
Runtime: runtime/mako_security.h (MakoSecret, mako_secure_zero).
unsafe bounds opt-out (Done)unsafe {
let v = unsafe_index(xs, i) // no debug bounds check — SAFETY: i in 0..len
}
Default indexing stays checked in debug (-O0 -g). Release may elide checks
under NDEBUG. Prefer unsafe { } only when you have proven the index is in
range.
Writers reject CR/LF/NUL and illegal name tokens (http_header_ok, Content-Type
in mako_http_reply_conn). Injection attempts fail closed.
sqlite_query_int / _params bind ? placeholders; arg count must match.pg_exec / pg_exec_params — use $1..$N; no concat-SQL happy path.if const_eq(got, want) == 1 { /* ok */ }
// alias: crypto_eq
Mako's session management, authentication, and authorization toolkit uses defense-in-depth to prevent common web security vulnerabilities.
Constant-time cookie and token checks. All session and auth comparisons use
const_eq internally, preventing timing side-channel attacks:
// All of these are constant-time by construction:
auth_session_cookie(cookie_hdr, "sid", expected) // session cookie check
auth_check_bearer(auth_hdr, expected_token) // bearer token check
auth_check_basic(auth_hdr, user, pass) // basic auth check
auth_token_check(token, secret) // HMAC-SHA256 token verify
csrf_check(expected, submitted) // CSRF token verify
HttpOnly cookie defaults. cookie_make always sets HttpOnly (prevents
JavaScript access via document.cookie), SameSite=Lax (blocks cross-site
POST requests from carrying the cookie), and Path=/. There is no API to
create insecure cookies -- safe defaults are the only option.
Cryptographic session IDs. session_id_new uses mako_random_bytes (backed
by the OS CSPRNG) to generate 16 random bytes, formatted as 32 hex characters.
This provides 128 bits of entropy -- brute-force guessing is computationally
infeasible.
CSRF token generation and verification. csrf_token generates a random token
for embedding in forms or response headers. csrf_check verifies it with
constant-time comparison. Combined with SameSite=Lax cookies, this provides
layered CSRF protection.
Secret wiping. Signing keys used with auth_token_sign and API tokens can be
stored via secret_from_str and explicitly zeroed with secret_drop. This
prevents key material from persisting in freed memory:
let key = secret_from_str("my-hmac-key")
let token = auth_token_sign("user:42", "my-hmac-key")
// ... after use ...
secret_drop(key) // zeroes memory via mako_secure_zero
HMAC-SHA256 signed tokens. auth_token_sign produces tokens in the form
subject.signature using HMAC-SHA256. The signature is verified by
auth_token_check in constant time. The subject can be extracted with
auth_token_subject without verification (always verify first).
Runtime: runtime/mako_security.h (MakoSession, MakoCSRF, MakoAuth).
crew exit calls mako_nursery_cancel_join — cancel flag set, then all tasks
joined. New kicks after cancel() do not start threads. Tests:
examples/testing/cancel_policy_test.mko.
In mako.toml:
[package]
name = "my-svc"
systems = true # ownership rules never weakened; gc ignored/forced off
# gc = true # app crates only — ignored when systems = true
Fingerprints cover full safety-relevant inputs; NLL never skipped on a partial fingerprint. See BUILD.md.
ResultA Result used as a bare statement is an error.
Debug: abort on OOB. Release: may elide (NDEBUG). Explicit opt-out: unsafe.
Lexer/parser/type errors print file:line:col, caret, and help: hints.
Mako does not claim "memory safe like a proof assistant." It claims active prevention of the failures that hurt backends most: ignored errors, overruns, leaked tasks, use-after-move, header/SQL injection footguns — with ownership rules that systems crates keep even if GC is enabled elsewhere.