AkurAI Build
Menu

AkurAI-Build

public
d8d22d186ffa74d1754b1d008ba6852637553fd7 29 commits 27 branches 10 tags
README.md

bunfork

bunfork is a Linux-first Rust web runtime with one CLI for verified static artifacts, server-rendered MiniJinja pages, SQLCipher migrations/backups, and bounded exhaustive vector search.

It hosts prebuilt static output from documented Svelte, SvelteKit, Next.js, Vue, Nuxt, and Vite configurations. It is not source-, build-, adapter-, or server-runtime-compatible with those frameworks: Bunfork does not embed JavaScript, Node, V8, JavaScriptCore, RSC, Nitro, or a framework compiler. Browser JavaScript in an admitted artifact is served byte-for-byte and runs only in the browser.

The default listener is 0.0.0.0:3100; port 3000 is never used by default.

Quick starts

Build Bunfork:

cargo build --release --locked

Run the native MiniJinja/vector application:

./target/release/bunfork keygen
./target/release/bunfork keygen --out .bunfork.token
./target/release/bunfork migrate
./target/release/bunfork dev

The database key and API token must decode to different 32-byte secrets; case-only variations of the same hexadecimal key are rejected.

Run a prebuilt static export with no database, key, token, migration, tenant, or model state:

./target/release/bunfork admit \
  --artifact frontend/dist \
  --manifest bunfork-static.json \
  --mode spa \
  --fallback index.html \
  --trailing-slash ignore

./target/release/bunfork doctor \
  --static frontend/dist \
  --manifest bunfork-static.json

./target/release/bunfork serve \
  --static frontend/dist \
  --manifest bunfork-static.json

One CLI

Global data options precede the command:

OptionEnvironmentDefault
--database <PATH>BUNFORK_DBdata/bunfork.db
--key-file <PATH>BUNFORK_KEY_FILE.bunfork.key
--tenant <ID>BUNFORK_TENANTdefault
--model <ID>BUNFORK_MODELdefault
CommandPurpose
keygen [--out <PATH>]Create a private 256-bit secret.
build --run <PROGRAM> --arg <ARG>... --output <DIR> --manifest <FILE> [artifact options] [--json]Run one explicit argv build command with a reduced environment, then admit its output. No shell or framework detection is inserted.
admit --artifact <DIR> --manifest <FILE> [--mode mpa|spa] [--base /] [--fallback FILE] [--trailing-slash ...] [--json]Scan a completed static directory and atomically write its deterministic manifest.
doctor --static <DIR> --manifest <FILE> [--json]Verify every file, byte length, SHA-256 digest, and manifest invariant offline. Exit code 3 means verification failed; the report names the first divergent file.
dev [native server options]Rescan native pages and re-read public assets per request. This is reload, not HMR.
dev --static <DIR> --manifest <FILE>Serve a frozen artifact without data state; rebuild/restart to see changes.
serve [native server options]Serve native pages and vector APIs. start is an alias.
serve --static <DIR> --manifest <FILE>Verify, preload, and serve a static artifact without opening SQLCipher.
migrateApply embedded migrations transactionally and verify the exact schema.
backup [--out <PATH>] [--json]Create, publish, and verify an encrypted no-overwrite backup.
restore <BACKUP> --yesVerify and restore a backup while preserving the previous live database.
vector put|search|delete ...Manage tenant/model-scoped encrypted vectors.
test [--pages <DIR>] [--public <DIR>]Run toolchain-free route, migration, vector, and backup smoke checks against a throwaway encrypted database. cargo test runs in CI and during deploy.
deploy [--out <DIR>] [--force]Build a digest-owned native bundle; default output is bunfork-dist.
deploy --artifact <DIR> --static-manifest <FILE> [--out <DIR>] [--force]Build a digest-owned static bundle with the Bunfork binary and systemd units.

Secret files take precedence over BUNFORK_DB_KEY and BUNFORK_API_TOKEN environment values.

bunfork-static-v1

Admission accepts an immutable directory of ordinary browser files and writes a strict manifest:

{
  "schema": "bunfork-static-v1",
  "root": ".",
  "mode": "mpa",
  "base": "/",
  "fallback": null,
  "trailing_slash": "directory",
  "precompressed": true,
  "immutable": true,
  "files": {
    "index.html": {
      "sha256": "…64 lowercase hexadecimal characters…",
      "bytes": 1234
    }
  }
}

Unknown/duplicate fields, extra/missing/mutated files, symlinks, hard links, special files, hidden/sensitive paths, malformed URLs, and traversal fail closed. Scans are bounded to 50,000 files, 256 MiB per file, and 512 MiB total; serving preloads the entire verified artifact after re-hashing, so admission and serving share one bound and post-bind filesystem swaps are eliminated.

Static HTTP behavior includes base paths, directory routes, .html exports, MPA 404 pages, explicit SPA fallbacks, asset/API fallback exclusions, GET/HEAD/405, ETags/304, conservative document caching, immutable caching only for semantic SvelteKit/Next namespaces, and quality-aware .br/.gz negotiation. Uncertain Vite/Nuxt/custom assets revalidate instead of relying on filename guesses. trailing_slash selects the canonical URL shape: directory redirects /guide to /guide/, redirect canonicalizes /guide/ to /guide, and ignore serves both without redirects. /_bunfork/health and /_bunfork/ready are reserved.

Producer configurations:

ProducerAdmitted outputBunfork mode
Svelte or Vue with Vitevite build output, normally distspa --fallback index.html --trailing-slash ignore for a client router; otherwise mpa
SvelteKitadapter-static output, normally buildmpa; use spa only with an explicit adapter fallback
Next.jsoutput: 'export' output, normally out; trailingSlash: true is recommendedmpa --trailing-slash directory
Nuxtnuxt generate / prerendered .output/publicmpa, or explicit spa for a client-only fallback export

.next/server, Next standalone, SvelteKit adapter-node, Vite SSR, and Nuxt/Nitro server output are rejected scope, not partially emulated features.

Native pages and assets

Only .html files below app/pages become routes:

FileRoute
index.html/
about.html/about
notes/[slug].html/notes/:slug
docs/[...path].htmlrequired terminal catch-all
docs/[[...path]].htmloptional terminal catch-all
(site)/about.html/about; route groups do not enter the URL

Repeated parameters, ambiguous routes, invalid templates, hidden entries, links, and special files fail startup/deploy. Production pages are compiled and loaded before binding, so later path swaps cannot expose files.

Templates in app/templates support MiniJinja inheritance/includes/macros; the sample uses base.html. _404.html and _500.html are optional error pages. Pages receive auto-escaped params, bounded multi-value query, pathname, tenant, and model. Files in public are validated, hashed, and preloaded before bind, then served below /assets without request-time filesystem reads.

Vector API

GET /api/health, /api/ready, /_bunfork/health, and /_bunfork/ready are public operational probes. Vector endpoints require Authorization: Bearer <API_TOKEN>:

PUT    /api/vectors/note-1
       {"id":"note-1","content":"hello","embedding":[1,0,0]}

POST   /api/vectors/search
       {"embedding":[1,0,0],"limit":5}

DELETE /api/vectors/note-1

Invalid identifiers, dimensions, norms, limits, and budgets are client errors rather than HTTP 500s. The exhaustive cosine-similarity scan is bounded to 4,096 dimensions, 10,000 candidates, 10,000,000 multiply-adds, 25 results, and 64 KiB content. JSON bodies are capped at 1 MiB. Bunfork does not load SQLite extensions or claim ANN/full-text search.

Encrypted data and recovery

SQLite is encrypted with bundled SQLCipher. Migrations are embedded and transactional; validation checks application/user versions, the exact migration ledger, exact table/index SQL, and rejects unexpected schema objects.

Backups use the same key as the live database; they are not key rotation. Backup/restore pin file identity with no-follow descriptors, reject links, sync publication, verify the exact encrypted schema, and preserve a timestamped pre-restore database. Stop the server before restore. Same-UID hostile races remain outside the filesystem guarantee; isolate untrusted code under another OS account/container.

Deployment

deploy tests, release-builds, stages, hashes, fsyncs, and atomically installs bunfork-dist. bunfork.json uses the bunfork-deployment-v2 schema and covers every bundled file except itself. --force validates that complete inventory before replacement. Hidden/protected outputs—including .git, .refrepos, app, src, data, backups, deploy, migrations, public, and target—are always refused. The prior verified release is preserved as a sortable bunfork-dist.previous-<unix-timestamp>-<pid> sibling and never automatically deleted; prune old siblings manually when disk space matters.

Native runtime state belongs outside the release:

./bunfork-dist/bunfork \
  --database "$HOME/.local/share/bunfork/bunfork.db" \
  --key-file "$HOME/.config/bunfork/db.key" \
  serve --token-file "$HOME/.config/bunfork/api.token"

Static bundles run from any working directory:

./bunfork-dist/bunfork serve \
  --static ./bunfork-dist/site \
  --manifest ./bunfork-dist/bunfork-static.json

The included native/static user units use %h/.local/lib/bunfork, 0.0.0.0:3100, and systemd filesystem/process hardening. Bunfork does not provision hosts, upload releases, configure DNS/TLS, or delete prior releases.

Security boundaries

  • 0.0.0.0:3100 is intentionally LAN-visible. Firewall it and use a trusted TLS reverse proxy before internet exposure; Bunfork has no built-in TLS.
  • Bearer and decoded-secret comparisons are constant-time. Browser mutations with Origin must match the configured public origin; without one, the Host-header fallback applies only to loopback listeners and Origin-bearing mutations are otherwise rejected. The bearer token remains primary API authorization.
  • Native CSP is strict. Static CSP permits same-origin code plus inline scripts/styles because official SvelteKit/Next exports require bootstrap inline code; use a reverse proxy with build-specific CSP hashes/nonces for a stricter policy.
  • Static mode has no data/secret dependency. A server-side foreign runtime is not supervised because a same-UID process could read Bunfork keys/database regardless of environment filtering.
  • Encryption does not protect a compromised live process, service account, root user, TLS proxy, binary, or unlocked key. There are no user accounts, roles, distributed locks, remote backup transport, or Windows hardening guarantees.

Verification and references

Ten read-only Codex CLI red-team passes and the final no-ship checklist are preserved in docs/adversarial. The P0 findings drove digest-owned deploys, secret byte comparison, descriptor-pinned recovery, strict artifact admission, and listener-level regression tests.

A second ten-architect architecture review, its per-commit remediation map, the consciously deferred backlog, and the bunfork-static-v1 schema evolution policy are preserved in docs/architecture-review and docs/static-schema-evolution.md.

Exact package versions and live results for official Svelte/Vite, SvelteKit, Next, Vue/Vite, and Nuxt builds are recorded in docs/framework-e2e.md.

Pinned research checkouts under .refrepos/ are excluded from compilation/deployment:

RepositoryCommitBehavior studied
Bunaaaa53f1compact CLI/runtime boundaries
Denoc537e012artifact/runtime distinctions and shutdown
Svelteb4d1583ae20fcompiled browser artifact boundary
SvelteKit5c38e515db7fadapter-static, fallbacks, routes
Next.js1bd2fd585aacexport/standalone split and static routing
Vuec0606e91798cSFC compiler versus browser output
Nuxt33f0fdb9d208generated public versus Nitro output
Vitec961cae2868cstatic output, base paths, hashed assets
Rari373fa706Rust routing versus V8/RSC boundary
Leptosf81ffcd5request-scoped Rust SSR patterns
Dioxuse1c63426deterministic routes and full-stack boundaries
Andromeda0ef48722Rust runtime and SQLite boundaries
Ponytail14a0d795minimal implementation discipline

Bunfork is MIT licensed; see LICENSE and THIRD_PARTY_NOTICES.md.