Skip to content

Developer overview

Editory is a monorepo of six services around one shared package. This page is the map; it’s enough to get the stack running and to know which service owns the thing you’re about to change.

Service Port Stack Owns
client 3000 Next.js 14, React 18, Redux Toolkit Web app, API routes, auth, Studio chat, the MCP endpoint
server 8000 Flask, LangGraph, Gemini, Azure OpenAI Script generation, editing agents, fact-check, source discovery
video-editor 7001 Flask, Whisper, MediaPipe, ONNX The video pipeline, its AI agents, capability endpoints
remotion-renderer 7002 Node 20, Remotion, Chromium Bundling and encoding; uploads the result
bias_calculator 8080 Flask, PyTorch Bias scoring
redis 6379 Redis Cache and session state

packages/video is the single source of truth for the video schema. The same Zod-validated VideoDefinition is used by the editor UI, the pipeline and the renderer — which is why it has to be built before anything else compiles.

flowchart TD
Browser["Browser<br/><i>Next.js · Redux · Remotion Player</i>"]
API["Next.js API routes<br/><i>:3000</i>"]
Server["server<br/><i>Flask :8000</i>"]
Editor["video-editor<br/><i>Flask :7001</i>"]
Renderer["remotion-renderer<br/><i>Node :7002</i>"]
Pkg["@stringer/video<br/><i>schema + components</i>"]
DB[("Postgres<br/><i>pgvector</i>")]
Blob[("Blob storage")]
Browser --> API
API --> Server
API --> Editor
API --> DB
Editor --> Renderer
Editor --> DB
Renderer --> Blob
Pkg -.->|shared schema| Editor
Pkg -.->|shared schema| Renderer
Pkg -.->|shared schema| Browser

The supported path is Docker. Give Docker Desktop at least 8 GB of RAM — the video services run Chromium and local ML models, and 4 GB fails in ways that look like random crashes.

  1. Terminal window
    docker compose --profile video up --build

    The video profile is the one you want for any video work. Without it you get the web app, the Flask API and Redis; with it you also get video-editor, remotion-renderer and video-builder (a watcher that rebuilds @stringer/video on change).

  2. 10–20 minutes. remotion-renderer pulls a full Chromium and video-editor downloads the matting model weights. Every start after that is seconds. Hot reload is on for all services.

  3. The app is at http://localhost:3000.

Terminal window
docker compose --profile video up -d # detached
docker compose logs -f client # tail one service
docker compose --profile video down # stop
docker compose --profile video down -v # ...and wipe volumes
docker compose build --no-cache video-editor # force-rebuild one image

Slower to set up, faster to iterate. Node 20 and Yarn 1 classic; install at the repo root because it’s a workspace monorepo.

Terminal window
yarn install --ignore-engines
# Build the shared package FIRST. Its dist/ is gitignored and both the client
# and the renderer import from it — nothing compiles until this runs.
yarn workspace @stringer/video build
yarn dev:video # terminal 1 — rebuild the package on change
yarn dev:client # terminal 2 — the web app
yarn dev:renderer # terminal 3 — only if you're rendering

Python services run on 3.11 (server, video-editor) and 3.9 (bias_calculator).

Every change ships with tests. New routes, agents, utilities or schema fields without coverage get rejected at review.

Terminal window
scripts/test/run-tests.sh all # every service, ~8s wall-clock
scripts/test/summary.sh # grep-friendly rollup
scripts/test/run-tests.sh client
scripts/test/run-tests.sh packages-video
scripts/test/run-tests.sh video-editor

Output is one line per service — TESTS: <service> passed=X failed=Y skipped=Z duration=Ws status=PASS|FAIL — with JUnit XML at <service>/test-results/junit.xml.

Where a new test belongs:

You added Test it in
A Next.js API route client/src/pages/api/**/__tests__/ (mock the DB, fetch, storage)
A Flask route A pytest route test via the Flask test client
A Python utility The service’s own tests/ directory
A schema field or Zod type packages/video/src/schema/__tests__/ — round-trip and integrity
A graphics category or Remotion component Vitest in packages/video and remotion-renderer
An agent or agent node A unit test with mocked LLM responses
feature/* ──PR──> development ──PR──> staging ──PR──> production
(light gate) (build images) (deploy)

The pipeline is change-detected — only services touched by the diff get tested and built.

Trigger What runs
PR → development Detect + unit tests
Push to development Nothing — the PR already validated the commit
Push to staging Unit tests + image build + smoke, then push to the registry
Push to production Retag the staging image and roll the resource

Promotion doesn’t require a PR; pushing to staging or production triggers the same pipeline.

Editory grew out of Stringer, a consumer news platform. The repository, some package names (@stringer/video, stringer-monorepo) and a few storage buckets still carry the old name, and parts of that original codebase are still in the tree but dormant — the consumer news reader, the bias pages, the scrapers under jobs/, and everything under legacy/.

That’s cosmetic. The product is Editory.