Skip to content

Latest commit

 

History

198 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Evidence Browser

CI Docker GHCR npm License: MIT

Stop dumping CI and agent test results into public HTML reports. Evidence Browser renders zip evidence bundles in an auth-gated, VS Code-like browser for teams reviewing logs, screenshots, reports, and run artifacts.

Evidence Browser bundle viewer with file tree

Short Evidence Browser walkthrough showing workspaces, bundle list, bundle viewer, and admin screens

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 auth, API keys, and optional OIDC SSO
  • 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
  • First-run setup/setup guides the initial admin, storage check, and workspace creation
  • eb CLI — Log in with an API key, create/validate bundles, upload evidence, and manage workspaces

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.

On a new instance, the app redirects to /setup until the first admin account and workspace exist.

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 upload examples/sample.zip --workspace 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)

First-run setup wizard

The /setup wizard appears automatically when an instance has no real admin or no workspace. It walks through:

  1. Admin — create the first username/password admin account.
  2. Storage — verify the configured local or S3/R2 storage adapter can be read.
  3. Workspace — create the first workspace slug, name, and optional description.

After setup, sign in with the admin account, open Settings to create an API key, then use that key with the eb CLI.

eb CLI quickstart

Install the CLI:

npm install -g evidence-browser-cli

Save your server URL and API key locally:

eb login http://localhost:3000
# API key: eb_...

Create and upload a bundle:

eb bundle create examples/sample-bundle --output /tmp/sample.zip
eb bundle validate /tmp/sample.zip
eb upload /tmp/sample.zip --workspace default --bundle-id sample

For CI, skip the local config file and pass credentials through environment variables:

EB_URL=https://evidence.example.com \
EB_API_KEY=$EVIDENCE_BROWSER_API_KEY \
eb upload dist/evidence.zip --workspace ci-results --bundle-id "pr-42-run-1"

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.
AUTH_LOCAL_ENABLED true Enables built-in username/password login. Set to false only when OIDC_ENABLED=true; API keys and active OIDC sessions still work.
OIDC_ENABLED false Enables OIDC authorization-code login. Requires issuer, client ID, client secret, and redirect URI.
OIDC_ISSUER OIDC issuer URL used for discovery, for example https://auth.example.com/application/o/evidence-browser/ for Authentik.
OIDC_CLIENT_ID Client ID from the OIDC provider.
OIDC_CLIENT_SECRET Client secret from the OIDC provider.
OIDC_REDIRECT_URI Public callback URL, for example https://evidence.example.com/api/auth/oidc/callback.
OIDC_SCOPES openid profile email Space-separated scopes requested during authorization. Include a provider-specific groups scope only when required.
OIDC_GROUPS_CLAIM groups Claim name read as the group list.
OIDC_ADMIN_GROUP Group whose members are synced as Evidence Browser admins. Other OIDC users are synced as user.
OIDC_ALLOWED_GROUPS Optional comma-separated allowlist. When set, users outside every listed group are rejected.
OIDC_AUTO_PROVISION true Creates a passwordless local user on first successful OIDC login.
OIDC_LINK_BY_VERIFIED_EMAIL false Links a verified OIDC email to an existing local user when no provider identity exists.
OIDC_BUTTON_LABEL Sign in with SSO Login page label for the OIDC button.

Authentik OIDC setup

Manual sign-off path recorded for issue #164 against Authentik 2026.8.0. The automated CI-compatible OIDC flow uses Dex in packages/web/e2e-oidc/oidc-flow.spec.ts; Authentik remains the first target IdP for operator setup and manual provider sign-off.

  1. In Authentik, create a Property Mapping for the admin group claim if the default groups claim is not already suitable:
    • Scope name: groups
    • Expression:
      return [group.name for group in request.user.ak_groups.all()]
  2. Create an OAuth2/OpenID Provider:
    • Authorization flow: the instance default explicit or implicit consent flow.
    • Client type: Confidential.
    • Redirect URI: https://<evidence-browser-host>/api/auth/oidc/callback.
    • Signing key: select the instance default.
    • Scopes: include openid, profile, email, and the groups mapping.
  3. Create an Application and attach the provider.
  4. Assign the Application to the users or groups allowed to sign in.
  5. Configure Evidence Browser:
    AUTH_SECRET="$(openssl rand -base64 32)"
    AUTH_LOCAL_ENABLED=false
    OIDC_ENABLED=true
    OIDC_ISSUER=https://<authentik-host>/application/o/<slug>/
    OIDC_CLIENT_ID=<provider-client-id>
    OIDC_CLIENT_SECRET=<provider-client-secret>
    OIDC_REDIRECT_URI=https://<evidence-browser-host>/api/auth/oidc/callback
    OIDC_GROUPS_CLAIM=groups
    OIDC_ADMIN_GROUP=evidence-browser-admins
    OIDC_ALLOWED_GROUPS=evidence-browser-users,evidence-browser-admins
  6. Verify that an evidence-browser-admins member lands in /admin, and that a user outside OIDC_ALLOWED_GROUPS is rejected with the safe login error.

The regression suite also covers the local-login off switch: with AUTH_LOCAL_ENABLED=false, POST /api/auth/login returns 403, the login page hides username/password fields, OIDC can still issue the normal evidence_session cookie, and API keys remain valid.

Upgrade note: migration v1 rebuilds the users table. Back up evidence.db before upgrading an existing instance.

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 read-only Bearer token for /api/mcp instance auth. Scoped eb_ API keys are also accepted. If unset, only informational tools are public unless AUTH_BYPASS=true.

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

The endpoint also accepts Authorization: Bearer <eb_...> scoped API keys. Read tools require a read, upload, or admin scoped API key; create_upload_url requires upload or admin. MCP_API_KEY is read-only instance access and cannot mint upload URLs. If MCP_API_KEY is unset, unauthenticated callers can use informational tools only.

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_workspaces Lists available workspaces
list_bundles Lists bundles in a workspace, optionally filtered by uploader, time range, and limit
create_upload_url Mints a short-lived signed multipart upload URL for upload/admin scoped callers
get_bundle_overview Returns bundle metadata, manifest, file tree, and inline index file content
get_bundle_tree Returns the file tree for one bundle
read_bundle_file Reads one text file up to 256 KB; binary or oversized files return metadata plus a web URL

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

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages