# Project export: DeceptiForge

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: DeceptiForge creates context-aware decoys across secrets, data, documents, and AI workflows—turning interactions into sensors that detect attacks, insider risk, and accidental AI leakage.
- Devpost: https://devpost.com/software/x-3ygjbz
- GitHub: https://github.com/akulhada/DeceptiForge
- Video: https://www.youtube.com/embed/8HOHHayxvO4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Advait Kulhada (157 commits), Claude Opus 4.8 (109 commits)

## Devpost submission (written by the team)

### Overview

Context-aware deception for the AI era What inspired me Most deception tooling still ships the same primitive: a lone fake credential dropped into a config file, waiting for someone to grab it. That works against a script grepping for AWS_SECRET. It fails against anything that reads context first, and increasingly the thing reading your repository is an AI agent that notices a stripe_key sitting in a folder with no billing code, no imports, and no history. An isolated honeytoken is a decoy that does not fit its own scene. I wanted the decoy to belong. If a payments repository gets a synthetic asset, it should carry that repository's vocabulary, live where a real engineer would have put it, and read as plausible enough that an agent exploring the codebase treats it as signal rather than noise. That reframing, from fake secret to believable synthetic business asset, is the whole idea. Everything else follows: infer the context, generate to fit, place where it is plausible, and detect the touch through a real monitoring pipeline instead of a mocked one. What I learned A security header can break your application in complete silence. I added a nonce-based Content-Security-Policy and the entire app stopped hydrating. No console error. No failing test. A statically prerendered page is built once with no nonce, while middleware issues a fresh one per request, and under strict-dynamic a nonce is the only thing that authorises a script, so 'self' stops applying. Every script was refused and React never took over. My first verification was wrong too: I read server-rendered form inputs as proof the client was alive. The only trustworthy signal turned out to be comparing the nonce in the served HTML against the one in the response header. That comparison is now a CI check. Expiry checks are security checks, and they fail like security checks. Writing sandbox tests surfaced a latent bug: authenticating an expired API key compared a naive stored timestamp against an aware one and raised TypeError. An expired key returned 500 instead of 401. Harmless until judge credentials, which always carry an expiry, made it reachable. Documentation that contradicts enforced behaviour is an operational defect, not stale prose. Several documents told operators the opposite of what startup validation enforced: REDIS_FAIL_MODE=open described as permitted, disabled auth described as merely rejected per request when it now refuses to boot. An operator following them would produce a deployment that does not start. I reversed an earlier architecture decision record rather than rewriting it, because an ADR records what was decided and why it changed. Configuration leaks through .env. Enabling one feature flag locally broke six unrelated production tests, because pydantic-settings reads that file and the value reached assertions that construct settings directly. Deriving the hardened-mode set from a single exported constant, instead of a copy-pasted literal, removed a whole class of drift. The through-line: in a security tool the dangerous failures are the quiet ones. The work was not making features exist. It was making the guarantees provable, on real HTTP responses, against a running system, in CI. How I built it A monorepo: FastAPI backend, Next.js dashboard, Plasmo browser extension, shared contracts package. The pipeline is a straight line you can watch end to end: scan → context → placement → generation → validation → monitoring → alert → incident Analysis is deterministic. It runs over bounded structured signals — languages, frameworks, services, naming patterns, sensitive-zone categories, AI surfaces — never raw source. Path-like values are descriptive metadata; the backend never opens them. Every inference reports its supporting evidence and a confidence value, so a reviewer sees why, not only what. Detection is signed and fails closed. Monitoring ingestion uses an HMAC scheme with a replay-nonce store and organization-scoped rate limiting. A touched decoy emits a signed event; the event becomes an alert only after verification; the incident is reconstructed from stored evidence rather than asserted. Severity and timeline come from application logic. GPT does two bounded jobs. It drafts synthetic content matching the inferred context, and turns a verified timeline into analyst-readable prose. It decides nothing that matters: not severity, not deployment, not whether an incident occurred. When the model is unavailable a deterministic fallback runs, and the interface labels it as such. Calibration uses Wilson score intervals rather than raw acceptance fractions, so a decoy accepted 3 of 3 times does not outrank one accepted 40 of 45. For observed acceptance $\hat{p} = x/n$ at confidence $z$, ranking trusts the lower bound $$ w^{-} = \frac{\hat{p} + \dfrac{z^{2}}{2n} - z\sqrt{\dfrac{\hat{p}(1-\hat{p})}{n} + \dfrac{z^{2}}{4n^{2}}}}{1 + \dfrac{z^{2}}{n}} $$ so small samples are penalised honestly and a cohort needs real evidence before it moves a weight. Anti-poisoning thresholds cap any single actor's contribution, and no learned weight reaches production without human approval. Deployment modes let one codebase serve as local development, a hosted evaluation sandbox, or a locked-down production tenant. APP_ENV is a closed set, so a typo fails startup instead of silently selecting middle behaviour. The hosted judge mode is production-hardened and differs only in which demonstration surfaces mount. Each evaluator receives a TTL-bound sandbox with its own generated organization, so no one can see another's work. Codex worked alongside me across implementation, migrations, tests, tenant-isolation and signed-ingestion review, CI debugging, and hardening. It was genuinely fast at mechanical breadth: wiring a router, drafting a migration, generating negative-path tests. It decided nothing load-bearing. Product direction, security boundaries, human-approval requirements, and final acceptance stayed mine. The pattern that earned its keep was adversarial: Codex proposes, I verify against the running system before believing it. Challenges I ran into One symptom, three unrelated causes. The judge workspace froze on its loading state. First cause: the nonce CSP blocking prerendered scripts. Second: next dev runs hot reload through eval and emits inline scripts with an empty nonce, and because a CSP containing a nonce makes browsers ignore 'unsafe-inline', my first fix did nothing at all. Third: two development servers sharing one build directory clobbered each other's chunks, so the page rendered correctly on the server and never hydrated. Identical symptom, no console error in any of the three. I found the last one only by counting attached React fibers between two servers running the same code: 21 on one, 0 on the other. Making the demo safe to host. The demo routes had no authorization at all. Fine while they were development-only; the moment a hosted evaluation mode could mount them, five mutating endpoints became unauthenticated writes on an internet-reachable deployment. Splitting reads open and writes credentialed took more thought than expected: requiring a credential to look at a fixed fictional story buys nothing, but letting anyone reshape what every other viewer sees is a real problem. Proving isolation instead of asserting it. It is easy to claim a sandbox cannot read files. Demonstrating it took a test that writes a marker into a real file, passes its path as a descriptive signal, and asserts the marker never appears in the response. If the backend had opened it, the analysis could leak the contents. Most of the isolation suite is shaped that way, and nearly all of its assertions are negative. Knowing when my own verification was insufficient. More than once I declared something working on evidence that did not support the claim. Server-rendered HTML mistaken for hydration. A contract test that silently went hollow after a refactor moved the string it matched. Catching those mattered more than any single feature, because a security tool whose guarantees are unverified is just a tool with confident documentation. What is next Repository and context metadata is still plaintext at rest while alerts and incidents are encrypted. Closing that gap is the next real change. Beyond it: a hosted deployment with restore-drill evidence, and widening the calibration engine from bounded demonstration toward measured, human-approved production use.

## README (from the GitHub repository)

# DeceptiForge

## Context-aware deception for the AI era

DeceptiForge analyzes repository and organizational signals, proposes believable synthetic business assets, explains where they should be placed, and reconstructs an incident when those assets are touched.

**OpenAI Build Week category:** Developer Tools.

**Public video:** https://youtu.be/8HOHHayxvO4

**Hosted judge workspace:** Distributed to judges through the submission channel. Its URL and credentials are intentionally not committed to this repository.

## Problem

Conventional honeytokens can be useful, but a generic credential often looks disconnected from the repository, documentation, and business workflow where it appears. That problem is more visible when engineers, coding agents, retrieval systems, and operational services encounter the same internal material.

## Approach

DeceptiForge normalizes repository and organizational signals such as technologies, naming conventions, documentation zones, infrastructure indicators, databases, and AI-facing surfaces. It builds a context profile, ranks sensitive zones, recommends plausible placements, and evaluates synthetic assets for safety and believability.

When a registered trace is observed, deterministic services validate the event, minimize evidence, create a deduplicated alert, and reconstruct an incident timeline. The core flow is **Context → Decoy → Placement → Detection → Incident**.

## Implemented features

- Repository intelligence, naming-pattern inference, organization context, sensitive-zone ranking, and explainable placement reasoning.
- Template-constrained secret, document, and database-record decoy concepts with deterministic safety and believability assessment.
- Trace registration, signed monitoring ingestion, replay protection, normalized alerting, deduplication, deterministic incident reconstruction, and bounded evidence.
- Organization-scoped API keys, roles, permissions, audit records, payload limits, and Redis-backed distributed replay and rate-limit controls.
- A restricted judge workspace with a TTL-bound fictional organization, per-session quotas, safe export, and reset isolation.
- An Interactive Analysis Lab for development and test, with ten fictional structured-signal scenarios, comparison, and JSON or Markdown export.

Feature-flagged deployment workflows, database honey records, AI/browser/agent sensors, measured coverage, SIEM export, reliability, and capacity controls are disabled by default and covered with deterministic fakes where applicable. They are not a claim of production certification.

## Architecture

Next.js provides the dashboard; FastAPI hosts the deterministic security pipeline. PostgreSQL stores organization-scoped artifacts, while Redis provides distributed replay and rate-limit state in hardened modes. Reconstruction is queued so ingestion remains on the hot path. GPT is optional and cannot change authoritative security facts.

## How GPT-5.6 Is Used

The runtime integration is model-configurable. It can use an approved OpenAI model to turn an already verified, deterministic incident timeline into an analyst-readable narrative. Input is minimized and sanitized, output is schema-validated and bounded, and model failure, missing credentials, or invalid output falls back to a deterministic narrative.

The current checked-in default is `gpt-5.6`, the GPT-5.6 Sol alias. Verify the rendered model badge in the recording before claiming GPT-5.6 on Devpost.

GPT does not assign severity, authorize deployment, accept monitoring events, alter evidence, choose an organization, or determine that an incident exists. Decoy generation is deterministic in the current implementation. A concrete demo example is a repository-trace touch that still produces its event, alert, severity, and incident when model access is disabled; only the analyst prose falls back.

## How We Built DeceptiForge with Codex

Codex accelerated codebase navigation, backend and frontend implementation, typed contract design, tests, migrations, tenant-isolation and signed-ingestion review, CI troubleshooting, dependency and container hardening, demo reliability, and documentation reconciliation.

The project author chose the security problem and context-aware deception thesis, required deterministic authority and human approval boundaries, rejected unsafe filesystem and hosted-demo shortcuts, selected the route model, and reviewed, revised, and tested generated changes. Codex was used to build and refine the project; it did not independently set product policy or security authorization.

## What We Added During OpenAI Build Week

The repository predates Build Week. The following verifiable extensions were added or substantially revised from July 13 through July 21, 2026:

- Platform-scope authorization separation, tenant-isolation coverage, signed-ingestion hardening, Redis fail-closed behavior, and production-shaped security tests.
- Interactive Analysis Lab and deterministic scenario comparison, added July 20 (`537fd80`, `fdc2123`).
- Dependency lockfiles, container and CI hardening, production topology validation, and operational worker readiness, added July 21 (`d561d80`, `f319daa`, `54a5157`).
- Explicit development, judge, staging, and production deployment modes; a restricted judge workspace; TTL-bound sandbox credentials; quotas; safe export; and isolated reset, added July 21 (`95903a5`, `3e7c0ef`, `5f93967`, `f4ade8c`, `5d327be`).

These commits are evidence of meaningful extension during the event period; they do not imply that the entire project was created during Build Week.

## Codex Session ID

Codex Session ID: `019f67eb-fa4d-77d0-ad70-eb034c57d246`

## Quick start

### Prerequisites

- Docker Desktop with Compose
- Node.js compatible with pnpm 9.15.4 and pnpm 9.15.4
- Python 3.12+ for local API tooling

```sh
git clone <repository-url>
cd DeceptiForge
cp .env.example .env
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env.local
pnpm install
docker compose up --build -d
docker compose exec api alembic upgrade head
pnpm --filter @deceptiforge/web dev
```

Open `http://localhost:3000`. The development templates enable fictional demo data. Select **Run DeceptiForge Demo**, then confirm the context, placement, synthetic validation, event, alert, incident, and narrative. A local smoke check is `curl http://localhost:8000/health`.

For detailed environment, API, and deployment instructions, see [Development](docs/Development.md), [Pipeline API](docs/Api.md), and [Deployment](docs/Deployment.md).

## Judge testing

Use the hosted judge URL supplied through the submission channel, not localhost. Use a current Chromium browser.

1. Receive a dedicated organization ID and API key through a trusted channel.
2. Open the root workspace and choose a fictional scenario.
3. Inspect the inferred context, sensitive zones, placement reasoning, and synthetic decoy concept.
4. Trigger the controlled interaction, then inspect the deterministic event, alert, incident, and evidence summary.
5. Review the analyst narrative and its deterministic fallback boundary.
6. Reset the sandbox. Reset affects only that sandbox and does not restore spent quota.

Judge credentials are provisioned out of band, shown once, organization-bound, and time-limited. A sandbox expires with HTTP 410; an operator must issue a new one. See [Judge access runbook](docs/runbooks/JudgeAccess.md). The sandbox uses fictional data, accepts bounded structured signals only, disables production connectors, and never scans a local path or clones a repository.

The curated `/demo` story is development/judge-only. `/analysis-lab` is development/test-only and returns 404 elsewhere.

## Installing the sensors (developer tools)

Two components install into a developer's own environment. Both are **disabled by default**: their
routers do not mount until the server flag is set, so the endpoints return 404 until then.

### Browser extension (AI-paste se

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 598 recognized source files, 2844 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 647)

```
.dockerignore
.editorconfig
.env.example
.env.staging.example
.github/workflows/ci.yml
.gitignore
.gitleaks.toml
.pip-audit-ignore
.prettierignore
.prettierrc.cjs
.trivyignore
apps/api/.dockerignore
apps/api/.env.example
apps/api/alembic.ini
apps/api/app/__init__.py
apps/api/app/agent_sdk/__init__.py
apps/api/app/agent_sdk/adapter.py
apps/api/app/agent_sdk/cli.py
apps/api/app/agent_sdk/client.py
apps/api/app/api/__init__.py
apps/api/app/api/admin.py
apps/api/app/api/agent_sensor.py
apps/api/app/api/ai_tripwire.py
apps/api/app/api/analysis.py
apps/api/app/api/browser_sensor.py
apps/api/app/api/capacity.py
apps/api/app/api/coverage.py
apps/api/app/api/database_honey.py
apps/api/app/api/demo.py
apps/api/app/api/deployments.py
apps/api/app/api/health.py
apps/api/app/api/integrations.py
apps/api/app/api/judge.py
apps/api/app/api/learning.py
apps/api/app/api/narrative.py
apps/api/app/api/onboarding.py
apps/api/app/api/pipeline.py
apps/api/app/api/reliability.py
apps/api/app/api/tenant.py
apps/api/app/config/__init__.py
apps/api/app/config/constants.py
apps/api/app/config/settings.py
apps/api/app/core/__init__.py
apps/api/app/database/__init__.py
apps/api/app/database/base.py
apps/api/app/database/session.py
apps/api/app/demo/__init__.py
apps/api/app/demo/acme-payments/.env.example
apps/api/app/demo/acme-payments/.github/workflows/ci.yml
apps/api/app/demo/acme-payments/Dockerfile
apps/api/app/demo/acme-payments/docs/architecture.md
apps/api/app/demo/acme-payments/docs/runbook.md
apps/api/app/demo/acme-payments/poetry.lock
apps/api/app/demo/acme-payments/pyproject.toml
apps/api/app/demo/acme-payments/src/billing-service.py
apps/api/app/demo/acme-payments/src/invoice-worker.py
apps/api/app/demo/acme-payments/src/payment-service.py
apps/api/app/dependencies/__init__.py
apps/api/app/jobs/__init__.py
apps/api/app/jobs/_runtime.py
apps/api/app/jobs/ai_tripwire.py
apps/api/app/jobs/coverage.py
apps/api/app/jobs/database_honey.py
apps/api/app/jobs/deployment.py
apps/api/app/jobs/incident_lifecycle.py
apps/api/app/jobs/learning_calibration.py
apps/api/app/jobs/reconstruction.py
apps/api/app/jobs/retention.py
apps/api/app/jobs/security_export.py
apps/api/app/main.py
apps/api/app/middleware/__init__.py
apps/api/app/middleware/cors.py
apps/api/app/middleware/observability.py
apps/api/app/models/__init__.py
apps/api/app/models/domain/__init__.py
apps/api/app/models/domain/agent_sensor.py
apps/api/app/models/domain/ai_tripwire.py
apps/api/app/models/domain/analysis_preview.py
apps/api/app/models/domain/analysis_signals.py
apps/api/app/models/domain/base.py
apps/api/app/models/domain/browser_sensor.py
apps/api/app/models/domain/coverage.py
apps/api/app/models/domain/database_honey.py
apps/api/app/models/domain/decoy.py
apps/api/app/models/domain/deployment.py
apps/api/app/models/domain/integrations.py
apps/api/app/models/domain/intelligence.py
apps/api/app/models/domain/interfaces.py
apps/api/app/models/domain/learning.py
apps/api/app/models/domain/narrative.py
apps/api/app/models/domain/operations.py
apps/api/app/models/domain/organization.py
apps/api/app/models/domain/reliability.py
apps/api/app/models/records.py
apps/api/app/prompts/__init__.py
apps/api/app/prompts/incident_narrative.py
apps/api/app/repositories/__init__.py
apps/api/app/repositories/agent_sensor.py
apps/api/app/repositories/ai_tripwire.py
apps/api/app/repositories/artifacts.py
apps/api/app/repositories/browser_sensor.py
apps/api/app/repositories/coverage.py
apps/api/app/repositories/database_honey.py
apps/api/app/repositories/deployments.py
apps/api/app/repositories/integrations.py
apps/api/app/repositories/learning.py
apps/api/app/repositories/reliability.py
apps/api/app/routes/__init__.py
apps/api/app/routes/router.py
apps/api/app/schemas/__init__.py
apps/api/app/schemas/api.py
apps/api/app/schemas/demo.py
apps/api/app/security.py
apps/api/app/services/__init__.py
apps/api/app/services/agent_sensor/__init__.py
apps/api/app/services/agent_sensor/classification.py
apps/api/app/services/agent_sensor/decoy.py
apps/api/app/services/agent_sensor/minimize.py
apps/api/app/services/agent_sensor/paths.py
apps/api/app/services/agent_sensor/rules.py
[527 more files omitted for size]
```

### Dependencies

- apps/api/pyproject.toml: alembic@>=1.14,<2, black@>=25.1,<26, cryptography@>=46.0.6,<49, fakeredis@>=2,<3, fastapi@>=0.115,<1, httpx@>=0.28,<1, mypy@>=1.14,<2, psycopg[binary]@>=3.2,<4, pydantic-settings@>=2.7,<3, pytest@>=8.3,<9, redis@>=5,<6, ruff@>=0.9,<1, sqlalchemy@>=2.0,<3, uvicorn[standard]@>=0.34,<1
- apps/extension/package.json: @eslint/js@^9.20.0, @types/node@^22.13.4, @types/react@^19.0.8, @types/react-dom@^19.0.3, eslint@^9.20.1, plasmo@^0.90.3, react@^18.2.0, react-dom@^18.2.0, typescript@^5.7.3, typescript-eslint@^8.24.0, vitest@^2.1.9
- apps/web/package.json: @deceptiforge/contracts@workspace:*, @eslint/js@^9.20.0, @radix-ui/react-slot@^1.1.2, @tailwindcss/postcss@^4.0.9, @types/node@^22.13.4, @types/react@^19.0.8, @types/react-dom@^19.0.3, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9.20.1, lucide-react@^0.468.0, next@^15.2.0, react@^19.0.0, react-dom@^19.0.0, tailwind-merge@^3.0.2, tailwindcss@^4.0.9, typescript@^5.7.3, typescript-eslint@^8.24.0, vitest@^2.1.9
- package.json: prettier@^3.5.3, turbo@^2.4.4
- packages/contracts/package.json: @eslint/js@^9.20.0, eslint@^9.20.1, typescript@^5.7.3, typescript-eslint@^8.24.0, vitest@^2.1.9

### Recent commits (newest first)

- fix(deps): clear the postcss advisory and declare overrides for both pnpm majors
- fix(deps): override sharp to a patched release
- chore(ai): default narratives to GPT-5.6
- docs(readme): add the public demo video URL
- docs(readme): add sensor installation and judge testing path
- fix(web): separate demo and tenant narratives
- docs(audit): add current feedback
- docs(readme): add Codex session ID
- fix(web): read the stored session after mount instead of during render
- merge: finalize Build Week submission guide
- docs(readme): prepare Build Week guide
- Merge pull request #11 from akulhada/feat/deployment-modes
- docs: correct guidance that contradicts enforced startup behaviour
- fix(web): give each dev server its own build directory, and drop the dev CSP nonce
- fix(web): relax script-src in development so `next dev` can hydrate
- fix(tests): derive the hardened-mode set from settings instead of importing conftest
- test(routes): cross-route isolation suite, and README route documentation
- fix(demo): open the read routes, keep the writes credentialed
- style(tests): wrap an over-long assertion in the demo access tests
- fix(demo): require a scoped credential for the curated demo once it is hosted

## Key source files (fetched from GitHub, selected and truncated for size)

### docs/WorkerScaling.md

```markdown
# Worker scaling

Scale workers from queue depth, oldest job age, drain rate, execution p95, database pool pressure,
and external API budgets—not CPU alone. Reserve capacity for P0/P1 monitoring, alerting, and
reconstruction. Use leases, readiness checks, graceful shutdown, and regional fencing before scaling
side-effect workers.

The reconstruction worker now shares a batch across organizations. Autoscaling policy is operational
configuration; no in-process autoscaler is introduced here.

```

### docs/CapacityPlanning.md

```markdown
# Capacity planning

Capacity recommendations are derived only from a passed `performance_runs` record. The current model
uses measured monitoring events-per-second-per-API replica and configured headroom; absent a passed
measurement it returns `uncertified`, rather than guessing.

Plan for API CPU <60%, workers <70%, database CPU/storage <65/70%, Redis memory <65%, and at least
2x the certified critical-ingestion burst. Record infrastructure topology with every performance run;
results from different topology are not comparable.

```

### package.json

```
{
  "_comment": "Purpose: root JavaScript workspace manifest. Responsibilities: shared scripts and tooling. Future modules: app-specific commands remain in their own manifests.",
  "name": "deceptiforge",
  "version": "0.1.0",
  "private": true,
  "packageManager": "pnpm@9.15.4",
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "lint": "turbo lint",
    "test": "turbo test",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "typecheck": "turbo typecheck"
  },
  "devDependencies": {
    "prettier": "^3.5.3",
    "turbo": "^2.4.4"
  },
  "pnpm": {
    "overrides": {
      "sharp": "^0.35.0",
      "postcss": "^8.5.10"
    }
  }
}

```

### docker-compose.yml

```yaml
# Purpose: run shared local infrastructure. Responsibilities: provide PostgreSQL for application modules. Future modules: add API and web services after their Dockerfiles exist.
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB:?Set POSTGRES_DB in .env}
      POSTGRES_USER: ${POSTGRES_USER:?Set POSTGRES_USER in .env}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
    # PostgreSQL is NOT published to the host by default. Local development adds the host port via
    # docker-compose.override.yml; production keeps the database on the private network only.
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
      interval: 5s
      timeout: 3s
      retries: 10
    volumes:
      - postgres-data:/var/lib/postgresql/data

  # Local Redis for the distributed rate-limit and replay stores. Not published to the host.
  redis:
    image: redis:7-alpine
    command: ['redis-server', '--save', '', '--appendonly', 'no']
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 3s
      retries: 10

  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    environment:
      APP_ENV: development
      CORS_ORIGINS: '["http://localhost:3000"]'
      DATABASE_URL: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
      # Exercise the Redis-backed stores locally (single worker still fine with in-memory).
      REDIS_URL: redis://redis:6379/0
      RATE_LIMIT_BACKEND: redis
      REPLAY_BACKEND: redis
    ports:
      - '${API_PORT:-8000}:8000'
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

volumes:
  postgres-data:

```

### packages/contracts/package.json

```
{
  "_comment": "Purpose: publish framework-neutral TypeScript domain contracts. Responsibilities: keep browser applications aligned on serialized core-domain shapes. Future modules: add only approved domain aggregates and event contracts.",
  "name": "@deceptiforge/contracts",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts"
  },
  "scripts": {
    "lint": "eslint src",
    "test": "vitest run --passWithNoTests",
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "@eslint/js": "^9.20.0",
    "eslint": "^9.20.1",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.24.0",
    "vitest": "^2.1.9"
  }
}

```

### apps/web/package.json

```
{
  "_comment": "Purpose: define the Next.js frontend package. Responsibilities: provide application scripts and frontend dependencies. Future modules: feature dependencies belong beside their owning capability.",
  "name": "@deceptiforge/web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint .",
    "test": "vitest run --passWithNoTests",
    "typecheck": "tsc --noEmit",
    "dev:judge": "NEXT_DIST_DIR=.next-judge NEXT_PUBLIC_APP_ENV=judge NEXT_PUBLIC_JUDGE_WORKSPACE_ENABLED=true NEXT_PUBLIC_DEMO_MODE=true NEXT_PUBLIC_API_URL=http://localhost:8001 next dev --port 3100"
  },
  "dependencies": {
    "@deceptiforge/contracts": "workspace:*",
    "@radix-ui/react-slot": "^1.1.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.468.0",
    "next": "^15.2.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "tailwind-merge": "^3.0.2"
  },
  "devDependencies": {
    "@eslint/js": "^9.20.0",
    "@tailwindcss/postcss": "^4.0.9",
    "@types/node": "^22.13.4",
    "@types/react": "^19.0.8",
    "@types/react-dom": "^19.0.3",
    "eslint": "^9.20.1",
    "tailwindcss": "^4.0.9",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.24.0",
    "vitest": "^2.1.9"
  }
}

```

### apps/extension/package.json

```
{
  "_comment": "Purpose: define the Plasmo browser-extension package. Responsibilities: provide build, lint, typecheck, and test scripts. Future modules: add runtime dependencies only when an extension capability owns them.",
  "name": "@deceptiforge/extension",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "plasmo dev",
    "build": "plasmo build",
    "lint": "eslint src",
    "test": "vitest run --passWithNoTests",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.20.0",
    "@types/node": "^22.13.4",
    "@types/react": "^19.0.8",
    "@types/react-dom": "^19.0.3",
    "eslint": "^9.20.1",
    "plasmo": "^0.90.3",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.24.0",
    "vitest": "^2.1.9"
  },
  "manifest": {
    "name": "DeceptiForge",
    "description": "DeceptiForge browser AI-paste sensor: detects synthetic protected content pasted into AI tools. No prompts, responses, history, or clipboard are captured.",
    "version": "0.1.0",
    "permissions": [
      "storage",
      "alarms"
    ],
    "host_permissions": [
      "https://chatgpt.com/*",
      "https://chat.openai.com/*",
      "https://claude.ai/*",
      "https://gemini.google.com/*",
      "https://copilot.microsoft.com/*",
      "https://github.com/*"
    ],
    "content_security_policy": {
      "extension_pages": "script-src 'self'; object-src 'self'"
    }
  }
}

```

### apps/api/pyproject.toml

```
# Purpose: define the FastAPI package and Python quality tools. Responsibilities: pin compatible runtime tooling and test configuration. Future modules: add domain dependencies only when their bounded context needs them.
[build-system]
requires = ["setuptools>=75"]
build-backend = "setuptools.build_meta"

[project]
name = "deceptiforge-api"
version = "0.1.0"
description = "DeceptiForge API infrastructure"
requires-python = ">=3.12"
dependencies = ["alembic>=1.14,<2", "cryptography>=46.0.6,<49", "fastapi>=0.115,<1", "httpx>=0.28,<1", "pydantic-settings>=2.7,<3", "psycopg[binary]>=3.2,<4", "redis>=5,<6", "sqlalchemy>=2.0,<3", "uvicorn[standard]>=0.34,<1"]

[project.optional-dependencies]
dev = ["black>=25.1,<26", "fakeredis>=2,<3", "mypy>=1.14,<2", "pytest>=8.3,<9", "ruff>=0.9,<1"]

[tool.setuptools.packages.find]
include = ["app*"]

[tool.ruff]
target-version = "py312"
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP"]

[tool.ruff.lint.flake8-bugbear]
# FastAPI expresses dependencies/params as call defaults; these are not mutable-default bugs.
extend-immutable-calls = [
    "fastapi.Depends",
    "fastapi.Query",
    "fastapi.Path",
    "fastapi.Body",
    "fastapi.Header",
    "app.security.require_scope",
]

[tool.black]
line-length = 100
target-version = ["py312"]

[tool.mypy]
python_version = "3.12"
strict = true
plugins = ["pydantic.mypy"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"
filterwarnings = [
    # Tracked: Starlette's TestClient warns that httpx>=0.28 support is deprecated in favor of a
    # future httpx2. This is a test-only dependency signal; revisit when httpx2 is released.
    "ignore:Using `httpx` with `starlette.testclient` is deprecated:DeprecationWarning",
]

```

### apps/api/Dockerfile

```
# Purpose: package the API service for a non-root, production-safe runtime.
# Responsibilities: install the backend, run as an unprivileged user, and start the server WITHOUT
#   auto-running migrations. Migrations are a separate, documented release step (see docs/Deployment).
# Pinned by immutable digest so a rebuild cannot silently pull different base contents.
# Human-readable tag: python:3.12-slim (update the digest and this comment together).
FROM python:3.12-slim@sha256:57cd7c3a7a273101a6485ba99423ee568157882804b1124b4dd04266317710de

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

# Dependencies come from a hash-verified lockfile, installed before the source for layer caching.
# --require-hashes makes pip refuse any artifact whose hash is not listed, so a compromised or
# substituted wheel fails the build instead of shipping. Regenerate with:
#   make lock            (see apps/api/README or docs/Development.md)
# The build context is the monorepo root so the API image can package the shared analysis fixtures.
COPY apps/api/requirements.lock.txt ./requirements.lock.txt
COPY apps/api/pyproject.toml ./pyproject.toml
COPY apps/api/app ./app
COPY apps/api/migrations ./migrations
COPY apps/api/alembic.ini ./
COPY packages/contracts/fixtures/analysis ./fixtures/analysis

RUN pip install --no-cache-dir --require-hashes -r requirements.lock.txt \
    && pip install --no-cache-dir --no-deps . \
    && adduser --disabled-password --gecos "" --uid 10001 appuser \
    && chown -R appuser:appuser /app

USER appuser

EXPOSE 8000

# Liveness probe via the standard library (no extra packages, no shell curl).
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
    CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health').status==200 else 1)"

# Production command: serve only. Run `alembic upgrade head` as a separate release step.
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### apps/api/app/demo/acme-payments/pyproject.toml

```
[tool.poetry]
name = "acme-payments"
version = "1.4.0"

```

[583 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]