> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praeto.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment Guide

> Deploy Praeto Dispatcher with Cloudflare Workers, Queues, and Neon Postgres.

# Deployment Guide

Praeto Dispatcher currently deploys as a Cloudflare Worker with a Cloudflare Queue binding and Neon Postgres as the database.

***

## Requirements

* Node.js installed
* npm installed
* Cloudflare Wrangler installed through `npx wrangler`
* Cloudflare account with Workers enabled
* Cloudflare Queue created
* Neon Postgres database

***

## Install dependencies

```powershell theme={null}
npm install
```

Required package:

```powershell theme={null}
npm install @neondatabase/serverless
```

***

## Wrangler config

This repo should use `wrangler.jsonc` as the primary Worker config.

Avoid maintaining both `wrangler.toml` and `wrangler.jsonc` long-term. If both exist, Wrangler may use the JSONC file depending on the repo state, which can cause confusion.

Example `wrangler.jsonc` shape:

```jsonc theme={null}
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "dispatcher-api-dev",
  "main": "src/index.ts",
  "compatibility_date": "2026-04-03",
  "compatibility_flags": ["nodejs_compat"],
  "observability": {
    "enabled": true
  },
  "upload_source_maps": true,
  "vars": {
    "ENVIRONMENT": "development",
    "SECRET_ROTATION_OVERLAP_DAYS": "7",
    "RETENTION_EVENT_DAYS": "30",
    "RETENTION_DELIVERY_DAYS": "30",
    "RETENTION_ATTEMPT_DAYS": "30",
    "RETENTION_DLQ_DAYS": "30",
    "USAGE_MONTHLY_EVENT_LIMIT": "10000",
    "USAGE_MONTHLY_ATTEMPT_LIMIT": "50000",
    "USAGE_MAX_ENDPOINTS": "1000",
    "USAGE_MAX_PAYLOAD_BYTES": "262144",
    "USAGE_MAX_FANOUT_ENDPOINTS": "100",
    "RATE_LIMIT_EVENTS_PER_MINUTE": "60",
    "RATE_LIMIT_ENDPOINT_CREATES_PER_HOUR": "30",
    "RATE_LIMIT_ADMIN_REQUESTS_PER_MINUTE": "120"
  },
  "queues": {
    "producers": [
      {
        "binding": "DISPATCH_QUEUE",
        "queue": "dispatcher-events-dev"
      }
    ],
    "consumers": [
      {
        "queue": "dispatcher-events-dev",
        "max_batch_size": 10,
        "max_batch_timeout": 5,
        "max_retries": 5
      }
    ]
  },
  "triggers": {
    "crons": ["0 * * * *"]
  }
}
```

***

## Secrets

Set database URL:

```powershell theme={null}
npx wrangler secret put DATABASE_URL
```

Set admin key:

```powershell theme={null}
npx wrangler secret put ADMIN_API_KEY
```

Then deploy:

```powershell theme={null}
npx wrangler deploy
```

***

## Database migrations

Apply migrations in order.

Important schema areas:

* `tenant_profile`
* `api_keys`
* `endpoint_destinations`
* `events`
* `deliveries`
* `delivery_attempts`
* `rate_limit_counters`

Phase 8 requires:

```sql theme={null}
CREATE TABLE IF NOT EXISTS rate_limit_counters (
  counter_key text PRIMARY KEY,
  tenant_id text NOT NULL,
  scope text NOT NULL,
  window_start timestamp without time zone NOT NULL,
  window_seconds integer NOT NULL,
  count integer NOT NULL DEFAULT 0,
  created_at timestamp without time zone DEFAULT now(),
  updated_at timestamp without time zone DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_rate_limit_counters_tenant_scope
  ON rate_limit_counters (tenant_id, scope);

CREATE INDEX IF NOT EXISTS idx_rate_limit_counters_window_start
  ON rate_limit_counters (window_start);

CREATE INDEX IF NOT EXISTS idx_rate_limit_counters_updated_at
  ON rate_limit_counters (updated_at);
```

***

## Deploy

```powershell theme={null}
npx wrangler deploy
```

Expected output should show bindings such as:

```text theme={null}
env.DISPATCH_QUEUE
env.ENVIRONMENT
env.SECRET_ROTATION_OVERLAP_DAYS
```

If expected vars are missing, Wrangler is probably using a different config file than you think.

***

## Tail logs

```powershell theme={null}
npx wrangler tail
```

Use this during tests and delivery debugging.

***

## Health check

```powershell theme={null}
Invoke-RestMethod "https://api.praeto.dev/health"
```

***

## Post-deploy smoke tests

```powershell theme={null}
.\tests\Web-Request-GetSafetyLimits.ps1
.\tests\Web-Request-GetUsageLimits.ps1
.\tests\Web-Request-TestIdempotency.ps1
.\tests\Web-Request-TestSSRFProtection.ps1
.\tests\Web-Request-GetMetricsSummary.ps1
```

***

## Production hardening checklist

Before a production deploy:

* [ ] Use separate production Neon database or branch
* [ ] Use separate production Cloudflare Queue
* [ ] Set production Worker name
* [ ] Rotate `ADMIN_API_KEY`
* [ ] Confirm all retention values
* [ ] Confirm all usage limits
* [ ] Confirm all rate limits
* [ ] Confirm logs do not leak webhook secrets
* [ ] Confirm endpoint signing secrets are never returned except create/rotate
* [ ] Confirm DLQ replay works
* [ ] Confirm retention dry-run works
* [ ] Confirm rate-limit SQL exists
