# Dependencies

This reference explains the direct third-party dependencies, their enabled
features and licences, and their impact on Scribe's formats. Dependency choices
keep storage and protocol code separate and use maintained libraries for
cryptography and platform integration.

Inspect the resolved dependency graphs from the repository root:

```sh
cargo tree -p scribe-server-cli -e normal,build --locked
cargo tree -p scribe-cli -e normal,build --locked
cargo tree --workspace -e features --locked
cargo tree --workspace --duplicates --locked
```

Versions correspond to the checked-in `Cargo.lock`. The `unsafe` column records
source-text counts of `unsafe ` in each crate's own sources, including comments
and conditional code. These counts indicate implementation size; they are not
a security audit or a count of reachable unsafe operations.

| crate | version | why std is insufficient | features | class | licence | unsafe | replacement / format impact |
|---|---|---|---|---|---|---|---|
| rpassword | 7.5.4 | hidden terminal password input and terminal-mode restoration on Unix/Windows; std cannot disable echo portably | defaults off; optional `scribe-platform/password-prompt`, enabled by client executable | normal (`scribe-platform` only) | Apache-2.0 | OS calls in upstream Unix/Windows terminal adapters; Scribe also wraps Carbon Secure Input in its macOS platform adapter; uses existing libc/windows-sys plus rtoolbox | replace behind `scribe-platform::terminal`; no stored/wire format changes |
| argon2 | 0.5.3 | salted and peppered, memory-hard password hashing and verification; std and fast content hashes cannot provide password storage | `alloc`, `password-hash`, defaults off; OS salt from existing `getrandom` | optional (`scribe-auth/server`) | MIT OR Apache-2.0 | 2 `unsafe ` occurrences in own Rust sources for x86 AVX2 dispatch; transitive primitives have separate implementations | maintained Argon2id/PHC-compatible implementation; pins v19, m=19456, t=2, p=1, 16-byte salt, 32-byte result; adds password records, no repository/object format changes |
| lz4_flex | 0.14.0 (exact) | LZ4 compression/decompression for object storage, transfer, and independent metadata pages; std has no compression codec | `std`, `safe-encode`, `safe-decode`; defaults off, no frame dependency | normal (`scribe-format`) | MIT | safe encoder and decoder selected; no application unsafe or native C; other disabled implementations contain unsafe | replace with a conforming LZ4 block implementation; block framing is specified by Scribe; identities hash original bytes, never compressor output |
| blake3 | 1.8.7 | optimized, tree-hashing content digest; std has no hash | `std` only (SIMD via build-time cc) | normal (`scribe-core` only; `scribe-format` dev-dependency for golden identity tests) | CC0-1.0 OR Apache-2.0 | 231 (SIMD) | never for existing data: every identity depends on it. Every call goes through `scribe_core::HashAlgorithm`, so a second algorithm (a FIPS digest, say) is one new variant applied to newly created repositories; the envelope algorithm byte refuses objects a binary cannot verify |
| redb | 4.2.0 | transactional embedded store with immediate durability | `std` | normal | MIT OR Apache-2.0 | 47 (mmap-free, file I/O) | logical export/import exists for engine replacement |
| fastcdc | 5.0.0 | maintained streaming FastCDC 2020 | none (no futures/tokio) | normal | MIT | 0 | profile 1 cut points are pinned; a new chunker is a new profile id |
| ignore | 0.4.33 | Git-style ignore rules and shared lock-policy globs; std has no glob compiler | none | normal (workspace and API, shared by clients/server) | Unlicense OR MIT | 0 | replace behind `IgnoreSet` / `LockPolicy`; lock matching is specified in the client guide, no stored object encoding change |
| getrandom | 0.4.3 | OS randomness for opaque ids | `std` | normal | MIT OR Apache-2.0 | 114 (OS calls) | any CSPRNG source; no format impact |
| libc (unix targets only) | 0.2.189 | declarations for `sendfile`, `fallocate`/`F_PREALLOCATE`, `posix_fadvise`/`F_RDAHEAD`, `FICLONE`/`clonefile`, none of which std exposes | std | normal (`scribe-platform` only) | MIT OR Apache-2.0 | 0 | every call is one audited `unsafe` FFI block in `crates/scribe-platform/src/sys/` with a std fallback in the same crate; no format impact |
| unicode-normalization | 0.1.25 | NFC for cross-platform path collision keys | none | normal | MIT OR Apache-2.0 | 5 | tables frozen per version; shared collision snapshots require the pinned path profile |
| tracing | 0.1.44 | structured diagnostics | `std`, `attributes` | normal | MIT | 7 | none |
| tracing-subscriber | 0.3.23 | `Targets` filter, fmt and JSON layers | `fmt`, `std`, `json`, `ansi` (no env-filter/regex) | normal (executables + scribe-log) | MIT | 26 | none |
| anstream | 1.0.0 | std detects terminals but does not adapt ANSI output to Windows consoles or strip styling from redirected streams | `auto`, `wincon` | normal (scribe-log; also shared with Clap) | MIT OR Apache-2.0 | 3 occurrences in own Rust sources; transitive platform bindings are separate | replace terminal stream adapter; no stored/wire format impact |
| tracing-appender | 0.2.5 | bounded non-blocking sink, daily rotation with retention | none | normal (scribe-log) | MIT | 0 | small; could be replaced by an in-tree channel writer |
| clap | 4.6.6 | argument parsing with usable errors | `std`, `derive`, `help`, `usage`, `error-context`, `suggestions`, `color` | normal (executables and shared scribe-log CLI parsing) | MIT OR Apache-2.0 | 0 | proc-macro at build time only |
| serde / serde_json | 1.0.229 / 1.0.151 | configuration and JSON diagnostics/reports; not wire or archive format | `std`, `derive` / `std` | normal (executables, scribe-api configuration, scribe-log json, saved TLS trust in `scribe-tls`, native FFI JSON in `scribe-ffi`, perf) | MIT OR Apache-2.0 | 2 / 15 | none on formats |
| toml | 1.1.5 | service configuration and shared typed `.scribe/config.toml` parsing; std has no TOML parser | `std`, `serde`, `parse`, `display` (unchanged) | normal (service executables and `scribe-api`, shared by clients/server/workspace) | MIT OR Apache-2.0 | (parser crates; unchanged dependency version/features) | replace behind `repository_config::RepositoryConfig`; versioned TOML schema, no binary object/index layout change |
| ctrlc | 3.5.2 | SIGINT/SIGTERM for graceful shutdown and client password-prompt cancellation; std has no signal API | `termination` | normal (executables and perf helper) | MIT OR Apache-2.0 | 15 (signal handlers) | shutdown-request file and cooperative stop checks replace handlers; no format impact |
| rustls | 0.23.45 | std provides no TLS or certificate verification; storage servers, gateways and the auth service need maintained TLS with client certificates | `std`, `aws_lc_rs`, defaults off; TLS 1.3 only, and every Scribe endpoint offers and accepts only the hybrid post-quantum key exchange `X25519MLKEM768`; 0.23.45 is the first release whose aws-lc-rs provider signs and verifies ML-DSA handshakes and certificates | normal (`scribe-tls`) | Apache-2.0 / ISC / MIT | 5 `unsafe ` occurrences in rustls Rust sources; cryptography comes from the aws-lc-rs provider (see its row) | replace transport/provider behind `scribe-tls`; a replacement must offer X25519MLKEM768 and ML-DSA (ring offers neither); storage protocol 4 and SAU2 transport only, existing content identities and persisted formats unchanged |
| aws-lc-rs | 1.18.1 | P-256 device key generation, PKCS#8 and SubjectPublicKeyInfo encoding and ECDSA signing for client certificates; ML-DSA-65 generation, PKCS#8 encoding, signing and verification for every CA and server key Scribe creates (`scribe_tls::MlDsaKey`); the same provider rustls uses, and the one that implements X25519MLKEM768; std has no cryptography | `aws-lc-sys`, `prebuilt-nasm`, `alloc`, defaults off (no FIPS, no `ring-io`/`ring-sig-verify` compatibility layers); `prebuilt-nasm` uses the crate's prebuilt NASM objects on Windows x86-64 only, so building there needs no NASM | normal (`scribe-tls`) | ISC AND (Apache-2.0 OR ISC); the aws-lc-sys C library it builds is `ISC AND (Apache-2.0 OR ISC) AND Apache-2.0 AND MIT AND BSD-3-Clause AND (Apache-2.0 OR ISC OR MIT) AND (Apache-2.0 OR ISC OR MIT-0)` | 367 `unsafe ` occurrences in own Rust sources (FFI calls into AWS-LC); transitive aws-lc-sys 0.45.0 has 12539, nearly all in its generated per-target bindings, and builds the AWS-LC C and assembly library from source at compile time (grep counts, not a security audit) | replace behind `scribe_tls::DeviceKey` and `scribe_tls::MlDsaKey` with any maintained provider that generates P-256 and ML-DSA keys and supplies X25519MLKEM768 to rustls; device keys are standard PKCS#8 and certificates standard X.509, and each credential row records the algorithm (`p256`), so another key algorithm is a new recorded value, not a change to existing credentials |
| zeroize | 1.9.0 | wipes buffers holding private keys (device keys, CA keys, key PEM and credential text) when they are dropped, in a way the optimizer cannot remove; std has no guaranteed wipe, and writing one needs `unsafe` volatile writes | `alloc`, defaults off | normal (`scribe-tls`, re-exported as `scribe_tls::Zeroizing` for other crates; already transitive through rustls, rustls-pki-types and aws-lc-rs; rcgen's optional `zeroize` feature is enabled so CA key pairs can be wiped too) | Apache-2.0 OR MIT | 18 `unsafe ` occurrences in own Rust sources (volatile writes and a compiler fence, the minimum a guaranteed wipe needs) | replace behind `scribe_tls::Zeroizing` with any wipe-on-drop wrapper; memory handling only, no format impact |
| rustls-webpki (as `webpki`) | 0.103.15 | reads the one SPIFFE URI SAN and the SubjectPublicKeyInfo of a client certificate; client certificate path validation runs through rustls' `WebPkiClientVerifier`, built on the same crate; std parses no X.509 | `std`, `aws-lc-rs`, defaults off | normal (`scribe-tls`; already transitive through rustls) | ISC | 0 `unsafe ` occurrences in own Rust sources | replace behind `scribe_tls::identity::peer_identity` with an X.509 reader that yields the same URI SAN and byte-identical SubjectPublicKeyInfo DER. The key id (full BLAKE3 of that DER, 64 hex) and the identity URI `spiffe://<trust domain>/user/<user id>/credential/<credential id>` (or `…/server/<server id>/…`) are Scribe formats stored at auth and carried in certificates; a parser that re-encoded the key would change every key id, and a different identity layout would need new certificates for every credential |
| tokio | 1.53.1 | scalable readiness-driven sockets, TLS waits, timers, tasks and bounded admission; std has no cross-platform async reactor | `rt-multi-thread`, `net`, `io-util`, `sync`, `time`, `macros`; no `full`/filesystem/process features | optional, server adapters only | MIT | 1068 `unsafe ` occurrences in own `src/**/*.rs` (including comments/cfg modules); transitive mio/socket2/bytes have separate unsafe implementations | replace scheduler/transport behind adapters; synchronous `RemoteOps`, canonical frames and stored formats unchanged |
| tokio-rustls | 0.26.5 | maintained async rustls transport; std and blocking rustls streams cannot yield while waiting for TLS bytes | `aws_lc_rs`, defaults off (no TLS 1.2, no `logging`) | optional, `scribe-tls/server` only | MIT OR Apache-2.0 | 0 `unsafe ` occurrences in own `src/**/*.rs`; shares the rustls aws-lc-rs provider | replace behind `scribe-tls`; same TLS 1.3 configuration and SAU2/storage bytes, no identity or format impact |
| ureq | 3.4.2 | synchronous HTTPS client for `scribe-runner`'s calls to the web API: connection pool, proxies, chunked and keep-alive handling; std has no HTTP client, and the web API is the one HTTP service a Scribe executable talks to | `rustls-no-provider`, `rustls-webpki-roots`, defaults off: rustls without a compiled-in provider, the aws-lc-rs provider supplied at runtime by `scribe-tls` (offers `X25519MLKEM768` first and also accepts `X25519`, which a proxy in front of the web API may offer alone), Mozilla roots via `webpki-roots`; no gzip, cookies, JSON or platform verifier | normal (`scribe-runner` only; the explicit exception to the no-HTTP rule in AGENTS.md) | MIT OR Apache-2.0 | 0 in ureq's own sources; transitive `ureq-proto`, `http`, `httparse`, `percent-encoding`, `utf8-zero`, `webpki-roots`, all Rust | replace behind `scribe_runner::http::Client` (one `post_json`); `--ca` swaps the public roots for private issuers; no format impact |

Per executable (`cargo tree -p ... -e normal,build`): `scribe` links no
`scribe-server`, `scribe-metadata`, or `redb`-based server code, because the
TCP crate's server adapter is behind the `server` feature, and no auth
database or service, which sit behind `scribe-auth/server` and
`scribe-auth/admin`. `scribe-runner` links the client crates, `scribe-tls` and
`ureq`, and no server code. All six executables share `tracing`,
`tracing-subscriber`, `clap`, and `serde_json` through `scribe-log`.

Tokio and tokio-rustls are enabled only through service features; a standalone
`cargo tree -p scribe-cli -e normal,build --locked` has neither. Workspace
builds may unify features with the server packages.
The blocking pool has an explicit thread cap and a semaphore acquired before
`spawn_blocking`, with one queued request per admitted connection. Storage
transfers take an additional permit before the shared worker permit so bulk
traffic cannot occupy every mutation worker. A separate query semaphore
reserves read capacity when mutations wait for publication or the database
writer; both capacities are included in the blocking-thread cap. redb and its immediate durability are
unchanged; async networking does not remove filesystem flush latency.

Duplicates: `syn` 2 and 3 (proc-macro build dependencies of `tracing-attributes`
versus `clap_derive`/`serde_derive`); build-time only, no runtime duplication.

Feature graph inspection (`cargo tree -e features`): `tracing-subscriber`
resolves without `env-filter` or `regex`; its `ansi` feature is enabled because
`with_ansi(true)` requires it. Clap enables `color`, sharing the `anstream`
adapter used by command output and stderr logs. Color support uses the following transitive packages: `anstream`, `anstyle-parse`, `anstyle-query`,
`anstyle-wincon`, `colorchoice`, `is_terminal_polyfill`, `nu-ansi-term`,
`once_cell_polyfill`, and `utf8parse`;
`fastcdc` resolves without `futures`/`tokio`; `redb` without `logging`.

Toolchain: pinned `1.96.0` in `rust-toolchain.toml`; MSRV declared `1.90`
(`File::try_lock`, let-chains); `Cargo.lock` committed; CI builds `--locked`.
Updates to `fastcdc`, `blake3`, `unicode-normalization`, and `redb` need a
review against the pinned fixtures (cut points, digests, collision keys, and
the engine version recorded in descriptors).

Path case folding uses committed Unicode 17.0.0 data, not an additional Cargo
dependency. `tools/generate-path-case-fold.js` regenerates the table from the
official `CaseFolding.txt` only after checking its pinned SHA-256. Its data is
licensed under Unicode-3.0; the license accompanies the generated table in
`crates/scribe-core/src/path/UNICODE-LICENSE.txt`. This supplies full folding
that std's case conversion cannot provide, adds no unsafe code or runtime I/O,
and has no enabled features. Its replacement or upgrade must retain the
specified collision profile or intentionally change that profile and its
index fixtures. Normalization and folding both use Unicode 17.0.0; exhaustive
scalar tests enforce normalization and idempotence.

Cryptography: rustls with the aws-lc-rs provider handles TLS 1.3, the hybrid
`X25519MLKEM768` key exchange, certificate chains, hostnames, and handshake
signatures, including the client certificates that carry every identity;
aws-lc-rs generates the P-256 device keys and the ML-DSA-65 keys of every CA
and server; rcgen, inside `scribe-cert`, issues server certificates and auth's
credential and access certificates, signing through those ML-DSA keys; rustls-webpki reads the SPIFFE
identity and public key of a verified client certificate; BLAKE3, always
through `scribe_core::HashAlgorithm`, produces public-key ids and CA
fingerprints plus keyed password-intent commitments; `getrandom` produces
peppers, salts and ids. RustCrypto `argon2` hashes passwords and verifies PHC
strings on the auth server, using `Argon2::new_with_secret` for the server-only
pepper; Scribe implements no cryptographic primitive. Argon2 and its PHC
encoding and digest dependencies are absent from the client executable's
normal dependency graph. TLS uses rustls, aws-lc-rs, and the webpki crates.
`Cargo.lock` still lists `ring` as an optional dependency of rustls-webpki, but
no enabled feature selects it: ring is not compiled.

## Certificate provisioning

`scribe-cert`, linked by both service CLIs and `scribe-ctl`, generates certificates
in process; the auth service also issues its credential and access certificates
through it. It exposes `init_ca` and `issue_server` library functions; CLI parsing
is a thin adapter. There is no external certificate executable, path setting, or
system installation requirement. The normal client build does not link rcgen or
x509-parser (client trust integration tests use the helper as a dev dependency).

| crate | version | why std is insufficient | features | class | licence | unsafe | replacement / format impact |
|---|---|---|---|---|---|---|---|
| rcgen | 0.14.10 | X.509 certificate generation and CA signing, for server certificates and for auth's credential and access certificates; it can generate but not load ML-DSA keys, so `scribe-cert` signs through `scribe_tls::MlDsaKey` via rcgen's `SigningKey` trait; std and rustls do not issue certificates | defaults off; `aws_lc_rs`, `pem`, `x509-parser`, `zeroize` (wipes CA key pairs on drop) | normal (`scribe-cert`); dev-only in `scribe-tls`, whose tests issue CA handover (cross) certificates | MIT OR Apache-2.0 | 0 `unsafe ` occurrences in own Rust sources; cryptography shares the aws-lc-rs provider | maintained certificate issuer behind `scribe-cert`; PEM remains standard X.509/PKCS#8, no repository/object/wire encoding changes |
| x509-parser | 0.18.1 | inspect imported CA constraints, validity, public key, and self-signature; rcgen's issuer import does not validate eligibility | defaults off; `verify-aws` (signature verification with aws-lc-rs for classical algorithms; it does not know ML-DSA, which `scribe-cert` verifies through `scribe_tls::MlDsa`) | normal (`scribe-cert`) | MIT OR Apache-2.0 | 0 `unsafe ` occurrences in own Rust sources; signature verification uses aws-lc-rs | maintained X.509 inspector behind `scribe-cert`; no repository/object/wire encoding changes |
| time | 0.3.55 | rcgen uses calendar-aware X.509 validity dates; std SystemTime does not represent these parameters | defaults off; `std`; other workspace users also enable formatting | normal (`scribe-cert`); already transitive via logging | MIT OR Apache-2.0 | 272 `unsafe ` occurrences in own Rust sources, including comments and feature-gated code; count is not an audit | date conversion library compatible with issuer API; no repository/object/wire encoding changes |

Certificate provisioning uses transitive PEM/ASN.1 encoding and X.509 inspection
dependencies. x509-parser internally uses nom;
Scribe does not introduce a general-purpose parser framework for its own formats.
CA inspection checks signing permission, lifetime coverage, key pairing, and a
self-signature. Server certificate verification stays inside `scribe-tls` using
the rustls verifier for the chain, server purpose, validity, and each DNS/IP name;
client certificates are verified there too, against auth's CAs.
Certificate tests and managed scenarios require no external certificate tools.

`scribe-ctl` reuses the listed TOML, Clap, serde/JSON, ctrlc, getrandom, and tracing
dependencies, plus the existing certificate, TLS, auth-admin, and platform crates.
Process detachment is isolated in
`scribe-platform` using its existing libc dependency on Unix.

`scribe-gateway` reuses the existing TCP server adapter, TLS, platform, metadata,
format, redb, TOML, Clap, serde/JSON, ctrlc, and tracing dependencies.
The gateway ledger uses redb immediate durability with a
separate [canonical routing record](/docs/gateway-format.md). Live instance disk-space
queries use `scribe-platform` through `scribe-server`. The client
contract suite uses the gateway as a dev dependency; the shipped client does not
link gateway or server implementation code.

`scribe-ctl` also reuses the internal `scribe-gateway` configuration API to
validate and supervise an optional gateway.

The shared repository configuration reuses the already pinned `toml` and `serde`
packages in `scribe-api`; it adds no third-party package or feature. Workspace
selection validation also calls this shared module. Profile and lock text are
validated as one TOML document; canonical object hashing still uses the exact
file bytes, and wire snapshots carry that text without serde-based binary encoding.

## TypeScript web workspace

The user-facing web application is a separate npm workspace. Its explicitly
requested HTTP frameworks and database clients do not add dependencies to the
Rust crates or alter their boundaries. Resolved versions and transitive packages
are pinned in `apps/web/package-lock.json`; inspect with `npm ls --all` from `apps/web/`.

The footprint below describes managed versus native code, not a security audit.
JavaScript has no Rust `unsafe` keyword; Node's TLS, cryptography and I/O still
use maintained native runtime libraries. No custom cryptographic primitive is
implemented. Feature selections refer to APIs used, since npm has no Cargo-style
feature gates.

| Package | Version | Purpose / why platform APIs are insufficient | Selected features | Licence | Native / unsafe footprint | Replacement / format impact |
|---|---|---|---|---|---|---|
| next | 16.3.4 | React routing, bundling and standalone production server; no equivalent in browser/Node std | App Router, client pages, standalone output | MIT | Native SWC compiler/build tooling; Node runtime | Another React host; HTTP URLs/contracts retained, no Scribe format impact |
| react | 19.2.8 | Declarative interactive UI and lifecycle | Client hooks, context | MIT | Managed JS | UI rewrite; no Scribe format impact |
| react-dom | 19.2.8 | React browser rendering/hydration | DOM renderer | MIT | Managed JS | Paired with React; no Scribe format impact |
| babel-plugin-react-compiler (dev) | 1.0.0 | Automatic React memoization through Next's compiler integration; platform APIs cannot analyze component dependencies | Next `reactCompiler`, React 19 runtime | MIT | Managed JS build tooling | Disable compiler and retain ordinary React rendering; no stored/wire format impact |
| @tanstack/react-query | 5.102.8 | Request deduplication, pagination, invalidation and query lifecycle | Queries, infinite queries, query client | MIT | Managed JS | Browser fetch/cache adapter; no Scribe format impact |
| motion | 13.2.0 | Coordinated React animation and reduced-motion integration | `motion/react` | MIT | Managed JS/browser animation APIs | CSS/Web Animations; no Scribe format impact |
| iconoir-react | 7.12.1 | Consistent accessible icon components | Individually imported React SVG icons | MIT | Managed JS/SVG | Alternate icon set; no Scribe format impact |
| react-markdown | 10.1.0 | Safe CommonMark rendering into React elements; platform APIs do not parse Markdown | HTML disabled, custom pinned repository URL handling | MIT | Managed JS | Another safe Markdown renderer; no stored/wire format impact |
| remark-gfm | 4.0.1 | Familiar tables, task lists, strikethrough and autolinks | GFM plugin for README rendering | MIT | Managed JS | Another GFM parser; no stored/wire format impact |
| codemirror | 6.0.2 | Full file editing: undo, selection, gutters, search, bracket matching; textarea lacks these behaviors | Lazy editor module, basicSetup, EditorView, no Tab focus trap | MIT | Managed JS/DOM | Another text editor or textarea; no stored/wire format impact |
| @codemirror/state | 6.7.4 | Immutable editor state and compartment reconfiguration without resetting undo history | Read-only facet, language/theme/options compartments | MIT | Managed JS | Paired editor state adapter; no stored/wire format impact |
| @codemirror/language-data | 6.5.2 | File-extension grammar discovery; browser APIs have no code grammars | Load only the selected file's language support | MIT | Managed JS and Lezer grammars | Explicit language bundles or plain text; no stored/wire format impact |
| @codemirror/theme-one-dark | 6.1.3 | Dark-mode syntax colors that remain legible on the editor surface | Theme compartment follows resolved application theme | MIT | Managed JS/CSS | Local syntax theme; no stored/wire format impact |
| sharp | 0.35.4 | Decode, validate, strip metadata and resize avatar uploads in the API; in the `assets-derive` sandbox, decode untrusted repository images (and tool and render output) for facts, thumbnails, placeholders and `view.webp`; Node has no image codec | API: bounded static JPEG/PNG/WebP input, WebP output. Sandbox: `failOn: "error"`, metadata and stats, auto-orient, resize, WebP output, run only in a network-less child process | Apache-2.0; its prebuilt `@img/sharp-libvips-*` binaries (libvips and codecs, 1.3.3) are LGPL-3.0-or-later, dynamically linked and shipped unmodified in the `api` and `assets-derive` images | Native libvips and image codecs; Node native addon | Image processing adapter or service; web avatar bytes and derived preview data only, no Scribe format impact |
| @rainbowatcher/toml-edit-js | 0.6.5 | Edit mount declarations without losing TOML comments; Node has no TOML document editor | API only, bounded 64 KiB configuration, parse/edit via Rust toml_edit in WASM | MIT | Sandboxed WASM and generated JS bindings; no native executable | Replace the configuration editing adapter; no canonical Scribe byte-format change |
| react-easy-crop | 6.2.3 | Accessible mouse/touch crop positioning and zoom without a custom gesture engine | Lazy-loaded square crop with circular preview | MIT | Managed JS/DOM | Another crop UI; no stored/wire format impact |
| lowlight | 3.3.0 | Syntax tokens without injecting generated HTML; no platform code grammars | Common grammars, dedicated worker, bounded text and timeout | MIT | Managed JS and highlight.js grammars | Another tokenizer; plain-text fallback; no stored/wire format impact |
| highlight.js | 11.11.2 | Explicit PowerShell and Windows batch grammars missing from lowlight's common set; no platform grammar API | DOS/PowerShell modules plus local Zig and Odin grammars, reused by the bounded highlighting worker | BSD-3-Clause | Managed JavaScript; already used transitively by lowlight | Replace tokenizer modules; no stored/wire format impact |
| tailwindcss | 4.3.3 | Utility styles and theme compilation | v4 CSS import | MIT | Build chain includes native Rust oxide scanner | CSS compiler/styles; no Scribe format impact |
| @tailwindcss/postcss | 4.3.3 | Tailwind integration with Next's CSS pipeline | PostCSS plugin | MIT | Transitive native scanner/CSS tooling | Alternate build integration; no Scribe format impact |
| fastify | 5.12.3 | HTTP route schemas, hooks, serialization and bounded requests | HTTP v1 routes, JSON Schema validation, pre-handlers | MIT | Managed JS; Node native HTTP underneath | Another HTTP adapter preserving v1 contracts |
| @fastify/swagger | 9.8.1 | Generate OpenAPI from live route schemas; platform has no OpenAPI generator | Dynamic OpenAPI 3, response schemas | MIT | Managed JS | Replace generator preserving v1 contract; no Scribe byte impact |
| @modelcontextprotocol/server | 2.0.0 | Maintained MCP protocol validation and stateless HTTP serving; no platform MCP implementation | McpServer, createMcpHandler | MIT | Managed JS with Node HTTP/crypto | Standards-compatible server; MCP transport only |
| @modelcontextprotocol/node | 2.0.0 | Adapt MCP web-standard handler to Node/Fastify with backpressure | toNodeHandler | MIT | Node streams/HTTP | Equivalent transport adapter; no canonical Scribe impact |
| zod | 4.5.4 | Typed validation of untrusted structured input; absent from platform APIs. MCP arguments and their generated JSON Schema, `.scribe/flows` definitions and job payloads in the shared service, and every message the `assets-derive` sandbox child returns to its supervisor | v4 object schemas, JSON Schema export (MCP) | MIT | Managed JS | Standard Schema-compatible validator; MCP, flow-definition, job-payload and sandbox-protocol contracts only, no Scribe byte impact |
| @modelcontextprotocol/client (dev) | 2.0.0 | Exercise MCP interoperability with official client rather than a simulated protocol | Client, StreamableHTTPClientTransport | MIT | Managed JS/Node fetch | External protocol conformance suite; no stored-format impact |
| @fastify/cookie | 11.1.2 | Correct cookie parsing/serialization and attributes | HttpOnly, Secure, SameSite cookies | MIT | Managed JS | Standards-conforming cookie adapter; browser session cookies only |
| @fastify/rate-limit | 11.2.0 | Bounded request admission per source | Global and route limits | MIT | Managed JS | Edge/shared limiter; no Scribe format impact |
| jose | 6.2.12 | Maintained JOSE/JWT/JWE encoding, verification and encryption | HS256 JWT; dir/A256GCM JWE | MIT | WebCrypto/Node native crypto; managed JOSE | JOSE-compatible implementation; web token/session format only |
| redis | 6.2.1 | Maintained RESP client with reconnect and command encoding | Valkey GET/SET with expiry | MIT | Managed JS; Node sockets | RESP client; disposable cache only |
| surrealdb | 2.0.8 | Typed maintained SurrealDB RPC, connection and query results | WebSocket RPC and parameterized SurrealQL, no ORM | Apache-2.0 | Managed client, Node network/crypto | DataStore repositories; sessions, profiles and durable web submit intents |
| yaml | 2.9.0 | YAML 1.2 parsing for `.scribe/flows` definitions; Node has no YAML parser | `parse` with alias and duplicate-key limits, YAML 1.2 core schema | ISC | Managed JS | Any YAML 1.2 parser; definition content is validated by zod afterwards, not by the parser |
| @noble/hashes | 2.4.0 | BLAKE3 is absent from Node crypto; verify Scribe canonical identities | BLAKE3, worker-thread content verification | MIT | Managed JS typed arrays | Conforming BLAKE3 implementation with fixtures; exact Scribe identities must remain unchanged |
| typescript | 5.9.3 | Static checking and compilation | Strict TS, declaration emit for SDK | Apache-2.0 | Managed build tooling | Compatible compiler; no stored/wire format impact |
| tsx | 4.23.13 | Execute TypeScript tests and watch API sources | Test runner, watch mode | MIT | Transitive native esbuild (Go) | Compile tests with tsc; no stored/wire format impact |
| oxfmt | 0.67.0 | Fast consistent TS/React/CSS/YAML formatting absent from platform APIs | Shared config, web/API overrides, check mode | MIT | Native Rust executable with upstream native dependencies | Another formatter; no canonical Scribe format impact |
| oxlint | 1.82.0 | Static correctness, React hooks, accessibility and Node lint checks | Root rules plus web React/Next/browser and API Node configs | MIT | Native Rust executable with upstream native dependencies | Another linter; no stored/wire format impact |
| @types/node | 26.6.3 | Node API type definitions | Node 26 | MIT | Type declarations only | Runtime-matched definitions; no format impact |
| @types/react | 19.2.18 | React API type definitions | React 19 | MIT | Type declarations only | Runtime-matched definitions; no format impact |
| @types/react-dom | 19.2.7 | DOM renderer type definitions | React DOM 19 | MIT | Type declarations only | Runtime-matched definitions; no format impact |
| three | 0.185.1 | Lazy WebGPU/WebGL 2 model viewer and maintained TGA/DDS/EXR/HDR parsers; model loaders for `@scribe/models`; model thumbnails in the `assets-derive` sandbox. Neither browsers nor Node have model or game-texture parsers | Browser: WebGPURenderer (WebGL 2 fallback), GLTFLoader with the meshopt decoder, RoomEnvironment, OrbitControls, ViewHelper; texture loaders in a disposable worker. `@scribe/models` (browser preview worker and sandbox): GLTF, FBX, OBJ/MTL, STL, PLY, USD and VOX loaders. Sandbox: `three/webgpu` renderer on the `webgpu` package | MIT | JavaScript; rendering uses the browser GPU driver, or Dawn in the sandbox; no native addon of its own | Replace the built-in viewers and model conversion; `view.glb` and thumbnails are derived, so no Scribe wire or stored-format impact |
| @types/three | 0.185.4 | Compile-time Three.js and addon contracts | Type declarations only | MIT | No executable/native code | Runtime-matched declarations; no format impact |
| @gltf-transform/core, /extensions, /functions | 4.5.0 | Read, write and optimise glTF 2.0 documents for `@scribe/models`, in the `assets-derive` sandbox and the browser's preview worker (facts from one document whatever the source format, the sandbox's `view.glb`, the browser's converted GLB); no platform glTF library | Document model, every KHR extension, meshopt compression and simplification, texture re-encoding through an injected encoder; `/functions` loaded only when optimising | MIT | Managed JS; no native code | Another glTF writer; `view.glb` is derived and swept, so no stored-format impact |
| meshoptimizer | 1.3.0 | Mesh simplification and EXT_meshopt_compression for `view.glb`; no platform mesh codec | Encoder and simplifier (WebAssembly) in the sandbox; decoder for EXT_meshopt_compression input in `@scribe/models`; the viewer uses three's bundled decoder | MIT | WebAssembly, no native addon | glTF Transform's other encoders; derived data only |
| draco3dgltf | 1.5.7 | Decode KHR_draco_mesh_compression input in the `assets-derive` sandbox | Decoder module only | Apache-2.0 | WebAssembly | Refuse Draco input; derived data only |
| webgpu (Dawn) | 0.6.1 | Render model thumbnails in the network-less sandbox with three's WebGPU renderer, on Mesa lavapipe without a GPU; Node has no graphics API | `create()` navigator.gpu, one device per child, read back one frame | MIT (Dawn BSD-3-Clause) | Native Node addon (Dawn) and the Vulkan loader, confined to the sandbox child | A software rasteriser for thumbnails; derived data only |

Bundled artwork, not an npm dependency: nineteen Noto Emoji SVGs in
`apps/web/apps/web/public/emoji/` (Copyright Google LLC, SIL Open Font License 1.1, licence
and notice alongside) draw quick reactions and profile status emoji identically on every operating system
instead of through the platform emoji font. They are static files served as
images, run no code and have no Scribe format impact; swap the directory to change
the artwork.

Docker service versions are pinned separately in their Dockerfiles. These are
independent executables, not Rust library dependencies: Node 26
(`node:26-bookworm-slim`; its OpenSSL 3.5 provides the `X25519MLKEM768` key
exchange every Scribe TLS endpoint requires) hosts web, API, content, worker
and `assets-index`;
SurrealDB 3.0.4 stores web sessions, flows and job records; Valkey 9.0.3
provides disposable caching, live events and job queues; OpenSearch 2.19.2
(`opensearchproject/opensearch`, Apache-2.0) holds the asset catalog, rebuilt
from Scribe when lost; SeaweedFS 4.47 (`chrislusf/seaweedfs`, Apache-2.0)
provides S3 object storage for run bundles and preview files; and nginx 1.29.6
routes the single browser origin. The `assets-derive` image (also run as
`assets-derive-sandbox-image` and `assets-derive-sandbox-scene`) is
`node:26-trixie-slim` with Debian's `ffmpeg`, `openimageio-tools`,
`mesa-vulkan-drivers` (lavapipe, a CPU Vulkan device for Dawn), `libvulkan1`
and `bubblewrap` (LGPL-2.0-or-later, not setuid), which jails each decoding
child in namespaces of its own (Node has no process jail). Trixie for OpenImageIO 2.5,
ffmpeg 7 and Mesa 25. They decode untrusted files only in the network-less
sandbox containers, each child inside its jail. Their own upstream licences
and native implementation footprints apply; Debian's ffmpeg is a GPL build. No
web image includes a Scribe binary.

## Editor support tooling

The VS Code extension has no bundled runtime npm dependencies. Its separately
locked build/type/test tools and their purpose, licence, native footprint, and
replacement paths are documented in
[editor dependency rationale](https://github.com/Flying-Rat/scribe/blob/main/integrations/vscode/DEPENDENCIES.md). These
tools are independent of the Rust workspace and web application.

## macOS presentation dependency

The native app pins [HighlighterSwift](https://github.com/smittytone/HighlighterSwift)
to **3.1.0** in `apps/macos/project.yml`. This is a UI-only Swift package, with no
Rust dependency or storage/protocol effect.

| dependency | purpose / why platform APIs are insufficient | features | licence | unsafe footprint | replacement / format impact |
|---|---|---|---|---|---|
| HighlighterSwift 3.1.0 (product `Highlighter`), bundled highlight.js 11.11.1 | Multilanguage syntax colors for native read-only diffs; AppKit lays out text but supplies no source-language grammars | Library and bundled JS/CSS only; fast attributed-string renderer; explicit filename language selection; no WebView, network, or execution of source text | MIT; highlight.js BSD-3-Clause; notices bundled in the app | No Scribe unsafe code; upstream Swift uses JavaScriptCore and Foundation/AppKit and has force unwraps; instantiated and serialized inside a dedicated actor, known-language and exact-source checks at boundary | Replace behind `ReviewSyntax`; source text and diff model remain independent; no disk/wire format impact |

Highlighting is bounded to 512 KiB combined. Failures display a plain-source
notice and never modify code or its line mapping. Full native text selection,
line gutters, edit alignment and navigation do not depend on syntax colors.
