Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deployment

Get Coop running on your own infrastructure. You may also be interested in the Docker images and architecture information.

Important

When you run migrations, we create a sample org which contains users with default passwords. Make sure you clean up in a production environment.

Self-hosting checklist

Coop does not currently ship a single production deployment recipe, but the repository does include the configuration surface you need to stand up a self-hosted instance. Treat the example environment files in db/.env.example, server/.env.example, and client/.env.example as the starting point for your deployment-specific configuration.

Required production configuration

At minimum, a production deployment should provide:

  • Database connectivity for the API server Postgres instance and the database migrator.

  • Redis connectivity for queues and background processing.

  • Scylla connectivity for item submission history.

  • Session secret (SESSION_SECRET) and other secrets.

  • A public UI origin such as UI_URL / VITE_UI_URL so generated links and browser-facing flows point at the correct host.

  • Email sender addresses that match your deployment.

You will usually also want to review the pool, timeout, TLS, and keepalive settings in server/.env.example before going live, since the defaults are tuned for local development rather than a long-running production environment.

Optional and deployment-specific configuration

Many other settings are only required if you are enabling specific features or changing backend choices:

  • Analytics and warehouse backends are controlled by WAREHOUSE_ADAPTER and ANALYTICS_ADAPTER. See Data Warehouse Abstraction Layer for the supported adapters and the related ClickHouse/PostgreSQL settings.

  • Child safety reporting is optional, but if you are using NCMEC reporting you must configure the org settings in Coop and set NCMEC_ENV=production on the server only when your deployment has been approved for live reporting. See NCMEC CyberTipline.

  • Client-side integrations such as Google Places and custom docs URLs are optional and can be left unset if you do not use those capabilities. See Content Proxy below for VITE_CONTENT_PROXY_URL.

  • Third-party integration keys in server/.env.example are generally optional unless you are enabling the corresponding integration.

Before going live

After the first successful migration and bootstrap:

  1. Remove or secure the sample org and any users created with default passwords.

  2. Confirm the production hostname and email settings are correct.

  3. Verify your selected warehouse and analytics adapters match the backing services you actually deployed.

  4. Leave NCMEC_ENV unset or non-production unless you intentionally want live CyberTipline submissions.

Single Sign-on

Coop supports single sign-on via SAML, e.g. with Okta. Enable SSO and configure the URL and certificate under Settings → Single Sign-on.

Example: Okta

Configuring Okta SAML for Coop requires:

  • Admin mode in Okta
  • Group names that match exactly between Okta and SAML
  • Admin permissions in Coop
  • Ability to create a custom SAML application

To set it up:

  1. Create a custom SAML application in Okta with the following settings:

    SettingValue
    Single sign-on URLYour organization’s callback URL (e.g. https://your-coop-instance.com/login/saml/12345/callback). Find this in Coop under Settings → SSO.
    Audience URI (SP Entity ID)Your Coop instance base URL (e.g. https://your-coop-instance.com).
    email attribute (in Attribute Statements)email. This depends on your identity provider’s attribute mappings (e.g. Google SSO may use “Primary Email”).
  2. In the Feedback tab, check I’m a software vendor. I’d like to integrate my app with Okta.

  3. In your app’s settings, go to the Sign On tab. Under SAML Signing Certificates → SHA-2, click Actions → View IdP metadata.

  4. Copy the contents of the XML file. In Coop, go to Settings → SSO and paste the XML into the Identity Provider Metadata field.

  5. On the same page, enter email in the Attributes section.

  6. In your Okta app under Assignments, assign users or groups to your app.

Content Proxy

Coop can optionally route item content URLs through a content proxy: a service you host that fetches a content URL and serves it back inside the review iframe, so it can control presentation and apply wellness features (since the review UI cannot otherwise style or control content from a cross-origin iframe).

Set VITE_CONTENT_PROXY_URL to your proxy’s base URL to enable it. Left unset (the default), matching content just loads directly in the iframe.

Coop does not ship a reference proxy implementation, so you’ll need to deploy your own implementation.

Load content

Coop loads your proxy at:

<VITE_CONTENT_PROXY_URL>/?contentUrl=<url-encoded content URL>

Your service should fetch and serve back the given contentUrl’s content.

Wellness features

After the iframe loads, and whenever a reviewer changes their overlay settings, Coop posts a message to your proxy’s page:

{
  "type": "customControl",
  "blur": 0,
  "grayscale": false,
  "shouldTranslate": false,
  "sepia": false
}

blur ranges from 0 (none) to 6 (strongest); grayscale and sepia are simple on/off filters. Your proxy’s page should listen for this message and apply the requested effects to the content it’s displaying.

Manual-review telemetry

The existing OpenTelemetry meter emits coop-api.manual_review.events.counter for successful enqueue calls, claims, stored skips/decisions and content-resolution outcomes. Attributes are event, queue_id, plus item_type_id when known; decisions also include decision_type, automatic and decision_source. decision_source is direct, sweep (including swept automatic closures), or automatic_close for non-sweep automatic closures. automatic remains true only for decisions containing AUTOMATIC_CLOSE; swept IGNORE and SAME_ACTION decisions remain false. Enqueue calls include BullMQ-deduplicated adds and are not unique-job counts.

coop-api.manual_review.duration_ms.histogram records phase=total_to_decision and human claim_elapsed using existing timestamps, with queue/item/decision attributes. Claim elapsed includes idle time; swept/automatic decisions omit it. Missing/invalid timestamps emit timing_unavailable_<phase> instead of zero. The counter declares unit 1 and the duration histogram declares unit ms. Caller attributes cannot override event or phase. Recording failures log a payload-free warning at most once per minute per meter instance; successful recording does not log and does not acknowledge exporter delivery.

No polling or extra storage reads are added. Continuous backlog/oldest age is not collected. Metrics are best-effort, not an audit ledger, completed enforcement or proof of viewing. Exporter failures cannot fail reviews. Configure cardinality and privacy in the deployment provider; no reviewer/content IDs, URLs or free-text reasons are emitted. Do not average per-host histogram percentiles.

Historical reference

For historical reference, AWS infrastructure code (CDK, Helm charts, Pulumi, CDKTF) that was previously used for production deployments is available in the 0.1 tag. That infrastructure code may have drifted from the current application architecture and is no longer maintained, but may serve as a reference for your own deployment.