---
title: "Set up Bicho"
url: "https://bicho.fyi/docs"
description: "Set up Bicho on Cloudflare Workers, create your first project, and connect your app. Follow the deployment guide to capture and investigate production errors."
---

Bicho is an error tracker you run in your own Cloudflare account. A Worker accepts exception and error logs, D1 indexes them as issues, and a private R2 bucket stores the captured evidence. You investigate through MCP or the HTTP API. Bicho doesn’t include a dashboard, and you don’t need an OpenTelemetry Collector.

Start by deploying the server and creating a project. Then connect the application you want to monitor. The server and application SDKs are separate packages; installing an SDK doesn’t deploy a server.

## Deploy the server

You need a Cloudflare account with R2 enabled. The hosted setup doesn’t require a local checkout.

[Deploy Bicho to Cloudflare](https://deploy.workers.cloudflare.com/?url=https://github.com/bicho-fyi/bicho).

The deployment flow copies the [server repository](https://github.com/bicho-fyi/bicho) into your account, provisions D1 and R2, and sets up Workers Builds. Its deploy command applies database migrations before deploying the Worker.

When prompted for `BICHO_ADMIN_TOKEN`, provide a unique token. Generate one on your own machine:

```
openssl rand -hex 32
```

Save it in a password manager. The server uses this token for API reads, project creation, source-map uploads, issue status changes, and instance MCP access. Keep it in a Cloudflare Worker secret, never in frontend code, D1, or a public configuration file. An unset or empty token makes admin endpoints return HTTP 503.

When the build finishes, save the Worker’s `https://<your-worker>.<your-subdomain>.workers.dev` address. This is your collector’s base URL. It isn’t `https://bicho.fyi`, which hosts this documentation rather than your errors.

## Connect MCP

Add your instance to a client that supports Streamable HTTP and custom headers. Clients use different configuration formats; this example fits clients with an `mcpServers` URL configuration:

```
{
  "mcpServers": {
    "bicho": {
      "url": "https://your-bicho-worker.example/mcp",
      "headers": {
        "Authorization": "Bearer <admin-token>"
      }
    }
  }
}
```

Replace the hostname and token with your own values. The [MCP guide](/docs/mcp/) covers transport requirements and available tools.

You can also connect the [public documentation MCP](/docs/mcp/#documentation-mcp) for help with setup. It doesn’t need a token and cannot see or administer your instance. Never send your instance credentials to it.

## Create a project

Use one project for each application or boundary you want to investigate separately. First call `list_projects` to see what’s already registered. Then call `create_project` with:

```
{
  "name": "My app",
  "hourly_limit": 1000,
  "secret": false
}
```

Only `name` is required. `hourly_limit` accepts an integer from 1 to 1000000 and defaults to 1000 new events per UTC hour. Set `secret: false` for browser applications, the current JavaScript SDK, and both modes of the Astro integration. The PHP, Rust, and Python SDKs and backend exporters can use `secret: true` to require an additional ingest secret.

The response contains:

-   `project.id`: a generated numeric-string identifier for API and MCP reads.
-   `otlp.endpoint`: your instance’s `/v1/logs` endpoint.
-   `otlp.headers`: `X-Bicho-Key`, plus `X-Bicho-Secret` for a secret-enabled project.
-   `sentry.dsn`: the DSN for an existing Sentry SDK. Secret-enabled projects also return `sentry.headers` for the backend transport.

Save the returned ingest headers and Sentry DSN immediately. Project listings don’t return credentials, and the server stores only a hash of an ingest secret. Every creation call makes a new project, even if the name matches an existing one. Check the project listing before retrying after a lost response. Bicho doesn’t provide a credential recovery or rotation endpoint.

If you prefer HTTP, send the same JSON to `POST /api/projects` with your admin bearer token. See the [HTTP API reference](/docs/api/).

## Connect your application

Already using Sentry? [Keep your SDK and replace its DSN](/docs/sentry/) with the generated `sentry.dsn`. Disable unsupported integrations; secret-enabled backend projects also require the generated transport headers.

Choose a Bicho SDK for your application:

-   [JavaScript/TypeScript and Node.js](/docs/javascript/): automatic browser capture and explicit Node.js reporting.
-   [PHP](/docs/php/): explicit exception/message capture with synchronous delivery.
-   [Rust](/docs/rust/): explicit error/message capture with awaited delivery on Tokio, including Tauri applications.
-   [Python](/docs/python/): explicit exception/message capture with synchronous delivery on Python 3.11+, using only the standard library.
-   [Astro](/docs/astro/): opt-in browser capture and SSR request-exception reporting, with separate project configuration.

For other languages or existing instrumentation, use an [OTLP/HTTP logs exporter](/docs/otlp/); Bicho accepts JSON and binary protobuf logs.

The [runnable Bicho examples](https://github.com/bicho-fyi/bicho-examples) cover browser, Node.js, PHP, Rust, Python, and Sentry on Node.js. Use a dedicated test project: running an example creates real error reports.

For a browser application:

```
npm install @bicho-fyi/javascript
```

Initialize in your browser entry point with the returned endpoint and `otlp.headers['X-Bicho-Key']`:

```
import * as Bicho from '@bicho-fyi/javascript';

Bicho.init({
  endpoint: 'https://your-bicho-worker.example/v1/logs',
  key: 'YOUR_PUBLIC_KEY',
  serviceName: 'my-app',
  environment: 'production',
  release: 'my-app@1.0.0',
});
```

This captures uncaught browser errors and unhandled promise rejections after initialization. The public key identifies the project; `serviceName` is application metadata, not authentication. The current JavaScript SDK doesn’t send `X-Bicho-Secret`, including when used in Node.js. Never put an ingest secret or admin token in a browser bundle.

See [SDKs and integrations](/docs/sdks/) to choose a client. Configuring an OTLP exporter doesn’t itself capture exceptions; your instrumentation must emit them as logs.

## Check the result

Send a clearly named setup event from your application:

```
Bicho.captureMessage('Bicho setup check');
const delivered = await Bicho.flush(5000);
```

This creates a real error-level occurrence in the project. Use a dedicated test project if you don’t want setup events in your production history. `captureMessage()` returning a UID means queued, not delivered. A successful `flush()` confirms the SDK’s delivery result, not that a particular issue was found.

Call `search_issues` with your generated `project_id`. Open the matching result with `get_issue`, then use `get_event` with a returned event ID to read its evidence. Check the message, service and environment. The SDK’s occurrence UID isn’t the server event ID.

If nothing appears, see [troubleshooting](/docs/operations/). Resolving a test issue hides it from the default unresolved listing but doesn’t delete its events.

## Local development

Use Node 24 and npm. Clone the server, install dependencies, and copy the example environment file:

```
git clone https://github.com/bicho-fyi/bicho.git
cd bicho
nvm use
npm ci
cp .dev.vars.example .dev.vars
```

If you don’t use nvm, install Node 24 another way and skip `nvm use`. Edit the ignored `.dev.vars` file to set `BICHO_ADMIN_TOKEN` to a fresh random token. Keep the value private.

Apply the schema to local D1, then start the Worker:

```
npx wrangler d1 migrations apply DB --local
npm run dev
```

Connect MCP at `http://localhost:8787/mcp`, or register a project through `POST http://localhost:8787/api/projects`, using the local admin token. Use the local project’s generated endpoint and headers in your application. Local D1 and R2 state are separate from hosted resources; a project created locally doesn’t exist on the hosted instance.

For development checks, run `npm run check` and `npm test` in the server repository. The test suite uses isolated local storage and doesn’t send errors to a hosted instance.

## Manual hosted setup

Use this route if you already manage your Cloudflare resources or CI configuration:

1.  Provision a D1 database and private R2 bucket. In the server’s `wrangler.jsonc`, replace the placeholder D1 ID and choose your resource names. The defaults are Worker/database `bicho` and bucket `bicho-events`.
2.  Keep the D1 binding named `DB`, the R2 binding named `PAYLOADS`, the `nodejs_compat` flag, and `migrations_dir: "migrations"`.
3.  Configure `BICHO_ADMIN_TOKEN` as a Worker secret. Leave `CORS_ORIGINS` empty unless you need trusted browser origins for admin access; ordinary cross-origin ingestion already supports preflight. See [credentials and privacy](/docs/privacy/).
4.  Set your Workers Builds or CI/CD deploy command to `npm run deploy`. It applies remote migrations through the `DB` binding before deploying.
5.  Check that the hourly Cron Trigger, `0 * * * *`, is attached. It resumes [source-map processing](/docs/sourcemaps/) when request-triggered work leaves a backlog.
6.  Create a project on the hosted instance through its API or MCP, then configure your application with those credentials.

The initial migration targets a fresh database. Editing a migration that already ran doesn’t upgrade that database. Don’t reset populated storage to reapply it.

For a custom domain, configure the Worker route and disable `workers_dev` when you no longer need the alternate URL. Preview URLs are disabled by default. Apply WAF and rate limiting rules to the entry points you actually expose; don’t use interactive challenges on ingest, API, or MCP paths. [Operations and limits](/docs/operations/) covers quotas, monitoring, and storage growth.

## Offline project generation

The server includes an optional generator for setups where you manage project SQL yourself:

```
npm run project -- 1 "My app" http://localhost:8787
npx wrangler d1 execute DB --local --file=_temp/project-1.sql
```

Run these commands in the server repository after applying the schema. Choose an unused positive numeric project ID. The generator writes `_temp/project-1.sql` and `_temp/project-1.json` without contacting Cloudflare; it won’t overwrite existing files. Applying the SQL registers the local project. Read its generated `otlp` object from the JSON file to configure ingestion.

The generator also accepts `--hourly-limit=1000` and `--secret`. For a hosted project, generate against the hosted origin and deliberately apply the SQL to the remote database instead. Generating or editing a JSON file doesn’t register anything. API or MCP creation is usually simpler and generates the project ID for you.

Keep generated SQL and JSON private: they contain ingest credentials. The repository’s `project.example.json` and `project.example.sql` contain placeholders, not credentials you can use.