Skip to content

hojinzs/evidence-browser

Repository files navigation

Evidence Browser

A read-only viewer for structured evidence bundles. AI agents and CI pipelines create zip bundles containing test results, reports, and artifacts — Evidence Browser renders them in a browsable, shareable interface.

Features

  • Bundle viewer — Browse zip bundles with file tree navigation
  • Rich rendering — Markdown (with embedded images), syntax-highlighted code, image preview
  • Pluggable storage — Local filesystem or S3/R2-compatible object storage
  • Authentication — Built-in username/password session auth for admin and API access
  • AI Agent integration/llm.txt endpoint and MCP server for programmatic access
  • Hierarchical bundle IDsorg/repo/pr-42/run-1 maps to nested storage paths

Quick Start

Docker

Run the published image with a random session secret and local bundle storage:

docker run -p 3000:3000 \
  -e AUTH_SECRET="$(openssl rand -base64 32)" \
  -v "$PWD/data:/data" \
  ghcr.io/hojinzs/evidence-browser:latest

Open http://localhost:3000 for the web app.

AUTH_SECRET signs admin sessions and must be a real random value for any shared or production instance.

Local development

# Install dependencies
npm install

# Copy and configure environment
cp .env.example .env.local

# Start API + web development servers
npm run dev

Open http://localhost:3000 for the web app. In a workspace, click Load demo bundle or run eb bundle upload examples/sample.zip -w default to render the bundled sample immediately.

  • Web dev server: http://localhost:3000 (Vite)
  • API dev server: http://localhost:3001 (proxied as /api from the web app)

Environment Variables

Auth

Variable Default Description
AUTH_SECRET evidence-browser-default-secret-change-me Session signing secret (must be explicitly set in production)
AUTH_BYPASS false Set to true only for trusted local or intranet deployments. All requests run as an admin user, /setup is skipped, and no login/API key is required. Do not expose an instance with this enabled to an untrusted network.

Storage

Variable Default Description
STORAGE_TYPE local local or s3
STORAGE_LOCAL_PATH Directory path (required when local)
S3_BUCKET Bucket name (required when s3)
S3_REGION auto AWS region or auto for R2
S3_ENDPOINT Custom endpoint (e.g. R2: https://<account>.r2.cloudflarestorage.com)
S3_ACCESS_KEY_ID S3 access key
S3_SECRET_ACCESS_KEY S3 secret key
S3_FORCE_PATH_STYLE false Use path-style URLs (for MinIO, etc.)

Limits & Cache

Variable Default Description
MAX_BUNDLE_SIZE 524288000 (500 MB) Maximum zip file size
MAX_FILE_COUNT 10000 Maximum files per bundle
MAX_SINGLE_FILE_SIZE 104857600 (100 MB) Maximum single file size
CACHE_TTL_MS 1800000 (30 min) In-memory cache TTL
CACHE_MAX_ENTRIES 50 LRU cache capacity

MCP

Variable Default Description
MCP_API_KEY Optional Bearer token for /api/mcp auth. If unset, the endpoint is public.

Bundle Format

A bundle is a zip file with a required manifest.json:

my-bundle.zip
├── manifest.json
├── index.md          # landing page (referenced by manifest)
├── logs/
│   └── output.log
└── screenshots/
    └── step-1.png

manifest.json

{
  "version": 1,
  "title": "PR #42 — Test Results",
  "index": "index.md"
}
Field Type Required Description
version number yes Bundle format version (use 1)
title string yes Displayed as the page title
index string yes Relative path to the landing file

Additional fields are allowed and passed through.

Bundle ID

Derived from the zip filename (without .zip). Supports hierarchical paths:

org/repo/pr-42/run-1  →  stored as  org/repo/pr-42/run-1.zip

Storage

Local Filesystem

STORAGE_TYPE=local
STORAGE_LOCAL_PATH=./data/bundles

Place bundles at {STORAGE_LOCAL_PATH}/{bundleId}.zip.

S3 / Cloudflare R2

STORAGE_TYPE=s3
S3_BUCKET=evidence-bundles
S3_REGION=auto
S3_ENDPOINT=https://<account>.r2.cloudflarestorage.com
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...

Upload bundles with key {bundleId}.zip.

AI Agent Integration

llm.txt

GET /llm.txt

Returns a plain-text guide describing the bundle format, storage configuration, upload instructions, size limits, and available API endpoints. Designed for LLM consumption (similar to robots.txt).

MCP Server

Evidence Browser exposes an MCP server via Streamable HTTP:

POST /api/mcp
Accept: application/json, text/event-stream
Authorization: Bearer <MCP_API_KEY>   # only if MCP_API_KEY is set

Available tools:

Tool Description
get_bundle_schema Returns manifest.json schema and zip structure
get_storage_info Returns storage type, bucket, endpoint, region (no secrets)
get_upload_instructions Step-by-step upload instructions for the current storage
list_bundles Lists available bundle IDs with optional prefix filter

Test with MCP Inspector:

npx @modelcontextprotocol/inspector http://localhost:3000/api/mcp

API Reference

All endpoints require authentication unless AUTH_BYPASS=true is explicitly enabled. AUTH_BYPASS=true is intended only for trusted local or intranet deployments: it skips /setup, treats every request as admin, and logs a startup warning. Do not expose a bypass-enabled instance to an untrusted network. Bundle routes are scoped to a workspace. Use the workspace slug for {ws}; {bundleId} is a single URL path segment, so encode reserved characters, including slashes as %2F, when constructing URLs.

Method Path Description
GET /w/{ws} Workspace bundle list page
GET /w/{ws}/b/{bundleId} Bundle landing page
GET /w/{ws}/b/{bundleId}/f?path={filePath} File viewer
GET /api/w List workspaces (JSON)
GET /api/w/{ws} Workspace details (JSON)
GET /api/w/{ws}/bundle List workspace bundles (JSON)
POST /api/w/{ws}/bundle Upload a bundle
GET /api/w/{ws}/bundles/{bundleId}/meta Bundle manifest + file tree (JSON)
GET /api/w/{ws}/bundles/{bundleId}/tree File tree only (JSON)
GET /api/w/{ws}/bundles/{bundleId}/file?path={filePath} Raw file content
GET /api/w/{ws}/bundles/{bundleId}/preview?path={htmlFilePath} Sandboxed HTML preview
GET /llm.txt LLM integration guide (plain text)
POST /api/mcp MCP Streamable HTTP endpoint

Deployment

Docker

AUTH_SECRET="$(openssl rand -base64 32)" docker compose up

The compose file builds the local Docker image, maps 3000:3000, and stores bundles under ./data on the host. AUTH_SECRET is required; generate a fresh random value instead of committing or reusing a fixed value.

For a local Node production build without Docker:

npm run build
npm run start

npm run build compiles:

  • packages/shared (shared types/utilities)
  • packages/api (Hono API server)
  • packages/web (Vite SPA), then copies it to root web/ for static serving by the API runtime

Tech Stack

License

License: MIT

This project is licensed under the MIT License. See LICENSE for details.

About

Authenticated viewer for agent-generated evidence bundles

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors