Fascicle is a library that wires LLM calls, tool calls, and subprocesses into
typed workflows. That puts it on the path of API credentials, model output, and
(via the claude_cli provider) a spawned child process, so the security model
below is worth reading before you wire it into anything sensitive.
Please report security issues privately. Do not open a public GitHub issue for a vulnerability.
- Preferred: open a private report through GitHub's security advisories ("Report a vulnerability").
- Alternative: email
hello@robmclarty.comwithfascicle securityin the subject.
Please include a description of the issue, the affected version, and a minimal reproduction if you have one. This is a small, single-maintainer project: expect an initial acknowledgement within a few days, and updates as a fix is worked out. Once a fix ships, credit is given in the release notes unless you ask otherwise.
Fascicle is pre-1.0 and ships breaking changes on minor releases. Only the latest published minor receives security fixes; there is no back-porting to older lines. Pin a version you have reviewed and upgrade deliberately.
| Version | Supported |
|---|---|
latest 0.x minor |
yes |
older 0.x lines |
no |
- The library never reads
process.env. Provider credentials are passed explicitly intocreate_engine({ providers }); reading them from the environment is the harness's job, done at its own boundary. An internal rule (no-process-env-in-core) enforces this acrosssrc/. See docs/configuration.md. - Credentials are not persisted or logged by fascicle. They live only in the engine config you construct and are handed to the underlying provider SDK.
- Trajectory and checkpoint files are sensitive. They capture prompts, model
output, and tool inputs/outputs in plain text. The bundled
filesystem_loggerandfilesystem_storeadapters write unencrypted files to disk; treat their output paths like any other secret-bearing artifact and keep them out of version control.
The claude_cli provider spawns the claude binary and parses its streamed
output. That is a real trust boundary:
- Under
auth_mode: 'oauth'the adapter scrubsANTHROPIC_API_KEYfrom the child environment so a stored session is used instead of leaking a key into the subprocess. Underapi_key/autothe child starts from an empty environment and only caller-supplied values pass through. - fascicle tools that carry an
executeclosure cannot run inside the subprocess. The defaulttool_bridge: 'allowlist_only'only adds tool names to the CLI's own allowlist and silently drops the closures; usetool_bridge: 'forbid'when you need a hard guarantee that noexecuteclosure becomes a silent no-op. - For untrusted work, confine the subprocess with
sandbox: { kind: 'bwrap' | 'greywall', network_allowlist, additional_write_paths }. An emptynetwork_allowlistmeans network-off. Full details in docs/cli.md.
Tools you pass to a model call run in your process, with whatever privileges your process holds. Validate their inputs and scope their side effects; fascicle does not sandbox in-process tool execution.
Every dependency you add is attack surface, and an agent library that handles
credentials and can spawn a subprocess is a high-value target. The npm ecosystem
has seen the whole range of the threat: compromised maintainer accounts that
publish a malicious version, malicious postinstall or preinstall scripts that
run automatically on npm install, and typosquatted transitive dependencies.
fascicle's architecture is deliberately shaped to keep both its own footprint and
yours small.
- No direct runtime dependencies. fascicle's
package.jsonhas an emptydependenciesfield.ai,zod, and every provider SDK are peer dependencies you install explicitly, so installing fascicle pulls in no transitive tree of its own. You choose and audit what actually gets added. - Provider SDKs are optional peers, loaded lazily. Installing fascicle does not drag in eight LLM SDKs; you install only the ones you call, and each is imported on first use rather than at load time.
- No install scripts. fascicle ships no
preinstall,install, orpostinstallhook, so the most common npm malware-execution path (code that runs automatically at install time) does not exist in this package. - A small, published artifact you can read. The package publishes only
dist,README.md,CHANGELOG.md, andLICENSE, as one package rather than a spread of separately publishable scoped packages. The diff you pin per release is small enough to review. pnpm auditgate and pinned overrides.pnpm check:security(--audit-level=high) tracks known advisories; transitive advisories are pinned withoverrides(for example,smol-tomlandfast-uri).- Provenance-attested CI publishing with no long-lived npm credential
anywhere. Releases are published by CI via npm Trusted Publishing (OIDC): the
registry trusts the release workflow's identity, minted per run, so no npm token
exists to steal or rotate. This removes the steal-the-CI-token vector that has
featured in several ecosystem compromises. The publish job pauses on a protected
GitHub Environment (
npm-publish) until a maintainer approves it, preserving the human gate that manual MFA-prompted publishing used to provide, and each release ships with a signed provenance attestation tying the tarball to this repository, commit, and workflow run. - Commitments that resist surface growth. No plugin registry, no integrations arms race, no hosted service: fewer moving parts, and fewer packages that can be independently compromised.
fascicle is a dependency like any other, so it is also a vector. The risks worth naming plainly:
- Single-maintainer key risk. fascicle is published by one maintainer, so a compromise of that account is the sharpest supply-chain risk to consumers, the same class of attack that has hit other agent frameworks. MFA reduces this risk; it does not remove it.
- Provenance proves where a release was built, not that it is safe. Every
release from v0.9.0 on carries an attestation you can verify with
npm audit signatures(or the provenance badge on npmjs.com), proving the tarball was built by this repository's publish workflow from a specific commit. It does not prove the source at that commit is benign; keep pinning exact versions you have reviewed. Releases published through the documented break-glass path (manual, when CI is unavailable) lack an attestation and are noted as such in the release. - fascicle does not vouch for your other dependencies. Vulnerabilities in
ai, a provider SDK, or their transitive dependencies remain your audit surface. fascicle avoids adding its own tree, but it cannot vet the peers you install alongside it. - Pre-1.0 support window. Only the latest published minor receives security fixes (see the Supported versions section above); there is no back-porting.
For the subprocess trust boundary (claude_cli spawning the claude binary), see
the Subprocess and sandbox model section above; a compromise of the claude binary
itself is outside fascicle's control.
The following are not fascicle vulnerabilities, though reports that show fascicle mishandling them are welcome:
- Vulnerabilities in a provider SDK, the
claudebinary, or a model endpoint itself. - Secrets leaked by your own harness reading
process.envand logging it. - Prompt-injection of a model you call. fascicle gives you the trajectory as an audit trail; deciding what a tool is allowed to do is your harness's job.