Documentation channel: current development main at compiler checkpoint 1d7e15e. The latest tagged release is v0.0.3; pages identify APIs that are not yet released.
Security model
Version: This page documents current-main / unreleased at compiler checkpoint 1d7e15e. Stable v0.0.3 predates the current-main crypto/encoding, streaming HTTP, WebSocket, cancellation, process-pipe, PTY, and P10 composition additions; those claims do not describe the stable release.
Strut separates the safe language surface from explicit unsafe and native boundaries. The goal is deterministic native programs without a tracing garbage collector while preventing the lifetime errors covered by the safe type system. It is not a sandbox, capability system, process supervisor, package registry trust service, or substitute for application authorization.
Safe memory
T* owns through reference counting, T& is a compiler-checked non-owning/non-null borrow, and weak_ptr<T> provides non-owning access to shared ownership. Safe null dereferences are checked rather than becoming native undefined behaviour. These guarantees target dangling references, use-after-free, double free, and covered null/lifetime errors.
Reference counting protects object lifetime, not logical invariants or unsynchronised shared mutation. Cross-thread state still requires mutexes, atomics, channels, or another documented synchronization strategy. Cycles of strong pointers must be broken explicitly with weak ownership.
Unsafe and FFI
ptr<T> operations and C ABI calls require an explicit unsafe boundary. Strut cannot prove that external native code respects lifetimes, bounds, thread safety, or ABI contracts. Static and dynamic native libraries inherit their own security properties. Keep unsafe regions small, validate data crossing them, and wrap them behind narrow safe APIs.
JSONIC, libcurl, and OpenSSL are the approved embedded third-party dependencies. SQLite can be consumed as an external/system library. This boundary limits dependency scope; it does not eliminate upstream vulnerabilities or deployment patching requirements.
Cryptography and encoding
The current-main public cryptographic surface is intentionally narrow and binary-first. secure_random_bytes uses OpenSSL RAND_bytes with no weak fallback. SHA-256 and HMAC-SHA-256 return raw 32-byte values. Equal-length secret comparisons use CRYPTO_memcmp; unequal public lengths can return false immediately.
OpenSSL failures become checked CryptoError values without exposing its error queue. Base64 and unpadded Base64url decoders reject whitespace, mixed alphabets, malformed lengths, misplaced or noncanonical padding, and non-zero unused trailing bits through EncodingError. Encoding alone has no OpenSSL dependency; crypto links libcrypto without libssl.
These primitives do not provide password hashing, key storage or rotation, authenticated encryption, certificate management, or protocol design. SHA-1 is not a public API. Its internal RFC 6455 handshake use does not make it available for application cryptography.
HTTP client limits
The current-main outbound client has one libcurl-backed policy. It accepts absolute HTTP(S) URLs, keeps certificate and hostname verification enabled, prevents an initially HTTPS request from redirecting to HTTP, validates methods/headers/options, reserves transport framing headers, and bounds buffered or streamed bodies and cumulative headers. Automatic content decoding is disabled so limits apply to delivered representation bytes.
Redirect handling does not constitute SSRF protection. Loopback, private, link-local, proxy-routed, and DNS-rebound destinations remain reachable unless the application authorizes destinations. When a URL is untrusted, disable automatic redirects and authorize every destination. Authorization and Cookie are not forwarded across a changed origin, but arbitrary custom headers can follow redirects and must not contain destination-bound secrets.
Streaming callbacks are synchronous and are the backpressure boundary. They must not retain borrowed assumptions or perform unbounded work. Cancellation is cooperative. A streamed body that cannot be rewound is not silently replayed for 307/308 redirects, and only final-response bytes are delivered.
HTTP server and WebSocket limits
The built-in server validates HTTP/1.0 and HTTP/1.1 request lines, fields, Host, Content-Length, Transfer-Encoding, chunk syntax, and framing before dispatch. Ambiguous TE/CL combinations, duplicate lengths, folded fields, controls, unsupported coding chains, malformed chunks, and non-empty trailers fail closed. Decoded body size and framing overhead are bounded separately. Response framing fields are transport-owned, and invalid handler metadata becomes a safe error or closes an already committed connection.
Connection and worker admission is bounded; synchronous streaming applies socket backpressure without an unbounded response queue. Pipelined requests run sequentially. Persistent reuse occurs only after validated request-body EOF and a complete self-delimited response. Limits reduce resource amplification but are not workload-specific rate limiting, authentication, authorization, or denial-of-service protection.
Request cancellation is scoped per request and is cooperative. Disconnect discovery can be delayed when a handler performs CPU-only work and no transport operation. Passing a request token to a child process cancels blocked pipe I/O; it does not terminate or reap that child.
RFC 6455 server handling validates masking, RSV/opcodes, canonical lengths, fragmentation, close codes, UTF-8, and configured frame/message limits before delivery or oversized allocation. One reader and one serialized writer may progress concurrently without an unbounded queue. Plain TCP uses independent directions. TLS uses one nonblocking owner thread with at most one bounded request per direction, exact-buffer retries, absolute request deadlines, and terminal handling for abandoned retries, timeout, interruption, or TLS failure; OpenSSL is never entered concurrently. Extensions, compression, and an outbound WebSocket client are absent.
Static-file responses accept an explicit trusted path. The helper does not safely map an untrusted request path, enforce a document root, prevent symlink escape, or protect a checked file from concurrent local replacement. Applications own path authorization and local filesystem threat policy.
TLS boundary
Outbound HTTPS uses libcurl and its configured TLS backend. Inbound HTTPS uses OpenSSL with TLS 1.2 or newer, validates the PEM certificate chain/private-key pair before opening the listener, and never falls back silently to plaintext. Plain HTTP and plaintext WebSockets do not select the OpenSSL server component. Certificate issuance, storage, rotation, revocation policy, ACME, and deployment termination topology remain operational responsibilities.
Processes
exec, process, and pty_spawn are argv-based: program names and arguments are not silently evaluated as shell text. exec_shell is a visibly explicit opt-in and must not interpolate untrusted data. Environment variables, working directories, executable search paths, inherited credentials, and the launched program itself remain trusted deployment inputs.
Process cancellation wakes blocked Strut pipe I/O but does not kill or wait for the child. Process pipes currently have no timeout. Callers must define termination, waiting, output limits, and descendant cleanup appropriate to their service. An argv-safe launch prevents shell injection; it does not make an invoked program safe against malicious arguments or input.
PTY limits
The current-main PTY API is binary-only and adds no terminal parser or emulator dependency. It does not decode text or sanitize terminal escape sequences. Applications that render untrusted PTY output must handle control sequences according to their own terminal/UI threat model. A PTY combines standard input, output, and error and therefore cannot preserve separate-channel assumptions.
Cancellation wakes blocked PTY I/O or wait with PtyError; it does not signal the child. POSIX close performs bounded TERM/KILL cleanup, but cannot atomically query and signal a foreground process group or signal every process in a session. Foreground transitions can race delivery, and background or detached groups can escape leader-group cleanup. Strong POSIX descendant containment requires an external supervisor or cgroup. External SIGCHLD reaping is unsupported.
Windows ConPTY requires Windows 10 1809 or Windows Server 2019. Child handles are non-inheritable, process creation uses bInheritHandles=FALSE, and the suspended child enters a kill-on-close Job before resume. Internal named pipes use 128-bit system randomness, first-instance enforcement, remote-client rejection, and an owner/SYSTEM-only DACL. Bare executable names resolve through the overlaid child PATH; relative entries are anchored to child cwd before an absolute application path is passed to CreateProcessW. ETX interrupt depends on console mode, hangup closes input, terminate discards unread output during bounded cleanup, and kill reports status 137. These are ConPTY lifecycle approximations, not POSIX signals.
P10 WebSocket-to-PTY composition is application code, not a security policy or privileged bridge. The application must authorize the upgrade before 101, validate binary terminal input and every text control, bound session count and timeouts, pass request cancellation into the PTY, and close and join the session on every terminal path. PTY output can contain hostile terminal control sequences, and an unbounded queue between the synchronous pumps would defeat the certified backpressure boundary.
Packages and supply chain
Package names, paths, URLs, and revisions are validated before filesystem or Git operations. Package paths cannot escape roots; symlinks and unsafe staged paths are rejected. Remote dependencies resolve to immutable Git commits, lockfiles record exact versions/revisions/SHA-256 content checksums and dependency edges, and verified cache entries are promoted atomically. Acquisition does not execute package-provided build scripts.
Official shorthand resolves only under the strut-packages organization; third-party Git dependencies require an explicit source. This is provenance policy, not a claim that official or locked code is benign. A checksum proves bytes match the lock, not that those bytes are secure. Review lock changes and dependency code, protect Git and release credentials, and patch/re-resolve when a trusted revision is vulnerable.
Offline install verifies existing locked cache content and performs no acquisition. Normal install may reacquire a missing or corrupt locked Git package at the same immutable revision. Local dependencies still rely on the integrity of their selected source and the machine performing acquisition.
Input and concurrency hardening
The compiler frontend has deterministic malformed-source fuzz smoke coverage. JSON, network, database, file, package, process, and PTY data remains untrusted and requires application-level size and semantic validation.
The async executor uses fixed workers and bounded queue admission with caller-runs saturation. This prevents an unbounded executor queue but can move work onto the submitting thread and does not replace endpoint-specific concurrency or rate limits.
Hardening
The project includes frontend fuzz smoke tests, sanitizer-backed bytes/crypto/HTTP/runtime checks, concurrency stress, FFI ABI fixtures, package certification, and cross-platform CI workflows. Windows PTY certification executes ConPTY spawn/I/O/control behavior, cancellation and close races, natural and final-owner descendant cleanup, 300-cycle churn, and version-aware HANDLE/thread return. P10 additionally covers plaintext/TLS composition, final-output drain, a 32 MiB slow-peer path, 1,000 sequential sessions, 32 concurrent sessions, disconnect and shutdown cancellation, and native-resource return. ThreadSanitizer still depends on a usable host runtime. Implemented behavior and local tests are not substitutes for a successful release matrix.
Security posture
Strut remains pre-1.0. Applications remain responsible for authorization, secret handling, resource policy, dependency updates, operating-system isolation, and deployment configuration.
