Architecture
Feedbacks is a modular monolith with independently built clients. The domain service owns authorization and transactions. HTTP, MCP and the CLI use the same operation registry rather than implementing parallel business rules.
flowchart LR
Browser[Web application] --> HTTP[HTTP transport]
Extension[Chrome extension] --> HTTP
Agent[MCP client] --> MCP[MCP transport]
CLI[JSON CLI] --> HTTP
HTTP --> Operations[Typed domain operations]
MCP --> Operations
Operations --> Access[Current identity and project grants]
Operations --> PG[(PostgreSQL)]
Operations --> S3[(Private object storage)]
Website[Public website] --> Docs[GitHub and documentation]Modules
config.ts,index.ts,db.ts,migrations.ts: validated environment, process lifecycle, connection pool and serialized migrations.auth.ts,accounts.ts,access.ts: account lifecycle, credential boundaries and project access.projects.ts,feedback.ts,views.ts,discussion-likes.ts: review workflow and optimistic concurrency.assets.ts,documents.ts: image normalization, bounded WebM intake, private storage, project document review and authorized readback.src/server/diagnostics/: private diagnostic upload and expiry inevidence.ts, authorized listing and readback inread.ts, archive streaming inarchive.ts, bounded event indexing inevent-index.ts, and the small public summary projection insummary.ts.scheduled-qa.ts: opt-in daily public-page checks, bounded private image comparison and reviewable run history.context.ts,export-limits.ts: versioned instructions, stable bounded exports and change cursors.operations.ts: transactional operation dispatch.app.tscomposes HTTP middleware and route order;src/server/http/owns operation dispatch and authorized asset downloads.mcp.tshandles the MCP transport.github-operations.ts: stable GitHub operation dispatch.src/server/github/groups connection changes, Issue requests, and status sync while retaining each authorization check and transaction around the external GitHub call.src/shared/contracts.ts: stable public import path for operation schemas and scopes.src/shared/contracts/domains/owns schemas by domain, andsrc/shared/contracts/registry.tscomposes the typed input/output registries.sdk/ios,sdk/android: optional native app clients using existing paired-device HTTP operations; neither owns authorization or embeds server credentials.
Client state is not authoritative. Database revisions detect stale writes; idempotency keys protect retries. Credentials are checked against current account state. Agents cannot become humans by selecting an input field.
The web application groups thread, project, member, account, document, survey, recording, and GitHub Issue views under src/web/threads/, src/web/projects/, src/web/members/, src/web/account/, src/web/documents/, src/web/surveys/, src/web/recordings/, and src/web/github/. Thread saved views live alongside the list filters; navigation, organization, screenshot comparison, diagnostics, and review evidence live under src/web/threads/detail/. The GitHub Issue dialog and status sync panel are separate components in the same feature folder. The recording viewer keeps data fetching, playback synchronization, and frame-saving state in thread-recordings.tsx; timeline projection/controls and replay/video/frame presentation live in focused sibling modules. Member credential handoff and administration live with the member views; password replacement lives with the account view. src/web/styles.css imports ordered feature styles so the cascade stays explicit. The extension keeps background.js, content.js, and editor.js as stable Chrome entrypoints; internal capture, submission, session, recording, video, review, connection, and diagnostics code lives in corresponding extension/ folders. The video page keeps recording and upload orchestration in video.js; capture health polling and crop pointer controls live in extension/video/. The session-review.js export path stays stable while review projection/sanitization and frame upload mapping live under extension/session/. The review controller injects review/anchor-evidence.js after the shared frame helpers and before content.js, so selector, fingerprint, and capture-context code stays in one module. The ZIP allowlist and isolated Chromium checks cover these internal paths. scripts/qa/extension/setup/, capture/, and review/ group browser fixtures and acceptance workflows; scripts/extension-browser-qa.mjs owns the disposable browser and final sequence.
The Help route, project readiness checks, personal agent setup, and Privacy page live under src/web/help/; the root web router imports those page components directly.
The extension worker keeps its manifest entrypoint and Chrome listener registration in background.js. Screenshot diagnostic capture state, raw debugger leasing, DOM evidence assembly, and context-bound retirement live in extension/diagnostics/worker-capture.js; the entrypoint supplies the existing session and account dependencies. The editor keeps draft persistence and submission in editor.js, while extension/diagnostics/editor-panel.js renders diagnostic evidence and owns its preview, download, selection, and masking controls.
Deployment model
Use one codebase and separate runtime installations. A deployment represents one organization. Its owners can administer that organization's projects and members. Separate origins or object prefixes do not turn a shared database into a tenant boundary.
| Surface | Deployable artifact | Data boundary |
|---|---|---|
| Public website | dist/site or Dockerfile.site | Static content; no application database or credentials |
| Team application | Dockerfile + PostgreSQL + private S3 | One organization per database |
| Hosted customer workspace | The same application image | Separate database, object-store credentials and runtime configuration |
| Browser extension | Versioned extension ZIP | User-selected server and local connection state |
| Native app integration | Host-built Swift or Android module | Host app pairs a reviewer device and stores its scoped token locally |
GitHub App credentials can remain in deployment secrets or be managed by human owners as encrypted PostgreSQL records with per-record keys in existing private AssetStore. The manifest handshake is single-use and session-bound; a same-origin callback bridge preserves Strict cookies and CSRF checks. Current catalog checks under the account lock and lazy key loading prevent disabled Apps from falling back to stale environment credentials. Project assignment is owner-only; approved-account policy is preserved on adoption/rotation. Each native Issue reservation and verified link pins its App identity; recovery never substitutes another App. See GitHub deployment configuration.
Organization-specific deployment inventory and secrets should live in a separate private infrastructure repository. A separate enterprise code fork is unnecessary for the current feature set and creates duplicated fixes. If commercial-only services are introduced later, keep their interfaces explicit and their licensing separate.
Scaling boundaries
The public website can be cached independently. The application stores sessions, grants, feedback and cursors in PostgreSQL, with screenshot bytes in object storage. Database connection count is bounded by DATABASE_POOL_MAX; budget it across all replicas and operational clients. Transaction advisory locks serialize migrations and conflicting account/project operations.
Three failed password sign-ins from one client IP within five minutes start a five-minute block. PostgreSQL stores this lock across restarts and replicas; the separate 30-request/minute account ingress ceiling remains process-local. Start with one application replica. Before adding replicas, enforce a shared ingress throttle with verified client-IP handling, tune the total connection budget, exercise concurrent writes on real PostgreSQL, and measure latency, error rate, memory and image-processing capacity under representative traffic. Adding replicas alone does not establish high availability.
The account lock serializes domain database operations, including export construction. Image upload performs an initial authorization and revision check under the lock, decodes the image and writes to private object storage outside the lock, then rechecks current authorization and revision before committing metadata. A rejected upload removes its uncommitted object where the transaction outcome is known. An uncertain database commit preserves the private object rather than risk deleting a committed image; operators should monitor and reconcile orphaned private objects. S3 requests have a 15-second abort deadline; PostgreSQL statements have a 30-second timeout and lock waits a 10-second timeout. Export creation has persistent per-user/project/deployment attempt and active-snapshot budgets plus content-size limits; pagination reuses an immutable snapshot. This bounds amplification but is not a throughput benchmark. Large image decoding and exports consume application resources. Set ingress body/time limits, monitor load and add bounded asynchronous processing only when measurements justify it. Retention, backups and restore objectives are deployment decisions; no automatic deletion policy is enabled.
Shared-database multi-tenancy would require tenant-scoped identity, tenant predicates on every query, tenant-aware tokens and cache keys, isolation tests and a migration plan. This release does not claim those properties.
Mechanically checked runtime boundaries
npm run check:harness parses literal runtime imports with the existing esbuild dependency. Shared modules depend on shared modules and Zod; server and web modules depend on their own layer and shared contracts; extension and site code stay inside their independently packaged directories. CLI modules use CLI/shared code, with these existing narrow exceptions:
src/cli/bootstrap.tsimports server config, database, migrations and authentication for the explicit operator entry point.src/cli/client.tsandsrc/cli/feedbacks.tsreuse the server'sDomainError.src/cli/mcp.tsreuses the server MCP adapter, passing the HTTP client as its executor.
These exceptions are exact file-to-file edges in the checker, not permission for arbitrary CLI imports into server internals. Extending one requires a documented reason and a regression test. The check excludes type-only and computed imports; review those explicitly. Internal domain layering within src/server remains a review responsibility.
The common integration catalog and credential vault separate provider metadata and encrypted storage from provider-specific registration, permissions and sync. GitHub is the first implemented adapter. Future providers require their own reviewed contracts and persistence; only implemented adapters are registered in the owner catalog.