1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages
DocForge2/docs/SECURITY.md

7.1 KiB

Security model

DocForge is a project-bound knowledge compiler. Its security boundary is an explicit project root, closed configuration, bounded data, and exact identities. It is not a general process or filesystem sandbox.

Start with core authority, then use policy precedence to decide which capabilities a server should expose.

Project and path confinement

Descriptors, reference-adapter configurations, canonical sources, authority files, templates, changesets, caches, indexes, previews, and declared outputs are resolved against one project root. DocForge rejects absolute paths where only project-relative paths are allowed, parent traversal, symbolic-link escapes, unsafe file types, and protected-root overlap. Important reads use no-follow file descriptors and compare file identity before and after reading.

Confinement protects DocForge operations. It does not stop another process with repository access from changing files. Long-running bindings revalidate descriptor and adapter implementation identity and require a restart after drift.

Generic canonical application stages backups and replacements below .docforge/application/transaction-* in mode-0700 directories. That private namespace confines ordinary path access and prevents access by other users. Deliberate arbitrary tampering by another process running as the same operating-system user is outside this boundary. DocForge still identity-checks private files before using or removing them, but mode 0700 is not isolation from the same UID.

Untrusted project content

Documentation, source text, templates, adapter metadata, compiler-database entries, and changeset content are data. They cannot redefine policy or instruct DocForge to execute a command. Generic manual rendering disables raw HTML, accepts a fixed template vocabulary, and rejects script-like content. Portable graph and manual workers accept validated inert packages and fixed built-in renderer identities.

The C++ reference adapter reads compile_commands.json only as bounded translation-unit inventory and fingerprint evidence. It never executes the recorded command, compiler, response file, or project program.

Adapter launcher boundary

AdapterLauncherV1 contains one Python module name and project identity. It contains no command, shell string, arbitrary argument list, working directory, environment, discovery rule, or callable selector.

Custom modules must be installed top-level modules. An isolated python -I probe resolves the module without importing it and requires its regular-file origin to remain inside the bound project. The sole trusted dotted exception is the packaged docforge.reference_mcp module. Client fragments use the exact validated interpreter and canonical fixed arguments with an empty environment.

This proves that the declared module is resolvable and project-bound. It does not make arbitrary module code safe. Project owners remain responsible for the implementation they install.

Mutation boundary

Normal MCP and the fixed reference server are read-only. Proposal tools exist only when a startup-bound writer is authorized by the descriptor. Canonical application exists only when a matching applier is explicitly configured.

Every proposal append, rebase, abandonment, and application is hash-bound. Application requires the exact changeset hash that was reviewed. Source identity, content hashes, permissions, conflicts, graph validity, and serializer round trips are checked before success. DocForge never turns prose approval into a fuzzy merge.

The generic applier compares exact canonical file identity immediately before each publication. Create uses no-clobber publication. Update and delete use atomic exchange and no-replace moves. Concurrent canonical-target mutation therefore fails closed, rolls back when the exact displaced state is still provable, or retains recovery evidence without overwriting foreign data.

This compare-and-swap protection is not a process-death journal. One file publication is atomic, and an in-process failure runs exact rollback, but an application spanning several canonical files does not promise crash atomicity if the process or host dies between publications. Operators must inspect canonical state and retained transaction evidence before retrying after such an interruption.

Derived state and publication

SQLite indexes, source-generation receipts, extraction caches, render fragments, previews, and portable artifacts are disposable. Corrupt, stale, foreign, oversized, or mismatched derived state is rejected or rebuilt from current project evidence.

Derived publication stages complete bounded output, flushes file and directory state, and commits with atomic replacement or no-clobber compare-and-swap. It is crash-safe: an interruption leaves the prior verified artifact, the complete new artifact, or explicit degraded post-commit evidence, not a mixed publication. Generated command-reference publication also serializes cooperating writers. A raced target is restored or retained for recovery instead of being silently discarded.

Projection and canonical-application lifecycles record when semantic content committed but later private cleanup or receipt verification degraded. Canonical success closes the applied proposal and persists compact application_recovery metadata with retained paths and remediation. A completed mutation is never reported as an ordinary retryable failure.

Limits and denial-of-service resistance

Inputs, results, traversal, context, changesets, renders, worker protocols, manifests, adapter assemblies, and extraction caches have explicit count and byte limits. Incremental extraction caches are capped at 10,000 sources and 64,000,000 bytes. Adapter primary nodes use the project max_nodes limit; edges and Logic have deterministic multipliers over that limit.

Limits reduce accidental and adversarial amplification. An in-process adapter can still allocate memory before returning data, so only trusted project-owned adapter code should run in the server process.

Secrets and network behavior

DocForge does not copy the parent environment into generated client fragments or detached projection workers. Doctor checks never return environment values. The live viewer binds to loopback, uses an unguessable URL token, supports read-only methods, and serves no arbitrary filesystem tree.

Project secrets must not be placed in canonical documentation, adapter configuration, compiler databases, templates, or changesets. Repository release gates include secret scanning, but that scan is not a substitute for credential hygiene.

No-AST boundary

--no-ast is a binding policy that preserves the selected adapter and prohibits Logic publication and retrieval. It is not a parser detector, filesystem sandbox, or promise that unrelated processes cannot parse source. See legacy and no-AST operation.

Reporting and recovery

Do not bypass a confinement, identity, policy, hash, or limit error. Preserve the failing evidence, stop the affected binding, and follow recovery and performance.