.github/9
ISSUE_TEMPLATE/3bug_report.md config.yml feature_request.md
workflows/3ci.yml clean-clone.yml release.yml
CODEOWNERS dependabot.yml pull_request_template.md
apps/630
api/250
src/243
common/30
__tests__/3authorize.test.ts bigint-serialization.interceptor.test.ts files-limits.test.ts
filters/1all-exceptions.filter.ts
guards/3auth.guard.ts internal-service.guard.ts rate-limit.guard.ts
interceptors/2actor-context.interceptor.ts bigint-serialization.interceptor.ts
middleware/2idempotency.test.ts idempotency.ts
pipes/1zod-validation.pipe.ts
actor-context.ts audit.service.ts cache.test.ts cache.ts cookies.ts core.module.ts deprecation.ts email.service.ts errors.ts event-bus.ts log-scrubber.test.ts log-scrubber.ts logger.ts permission-cache-bridge.ts prisma.service.ts redis.service.ts trace-metadata.ts types.ts
config/1env.ts
lib/11
__tests__/5crypto.test.ts filter-builder.test.ts jwt.test.ts pagination.test.ts positions.test.ts
authorize.ts crypto.ts filter-builder.ts jwt.ts pagination.ts positions.ts
modules/197
ai/28
__tests__/4ai-writing.test.ts database-ai.test.ts generate-executor.test.ts prompts.test.ts
generator/4executor.ts generate.controller.ts generate.dto.ts generate.service.ts
ai-chat.service.ts ai-rate-limit.ts ai-writing.controller.ts ai-writing.dto.ts ai-writing.service.ts ai.controller.ts ai.module.ts ai.service.ts budget-guard.service.ts database-ai.controller.ts database-ai.dto.ts database-ai.service.ts db-prompts.ts dto.ts gateway-provider.ts ledger.service.ts prompts.ts rag.controller.ts rag.dto.ts rag.service.ts
api-keys/3api-keys.controller.ts api-keys.module.ts api-keys.service.ts
audit/2audit-log.controller.ts audit.module.ts
auth/4auth.controller.ts auth.module.ts auth.service.ts dto.ts
automations/5automations.controller.ts automations.module.ts automations.service.ts dto.ts webhook-incoming.controller.ts
blocks/5
__tests__/1batch-version-cas.test.ts
block.service.ts blocks.controller.ts blocks.module.ts dto.ts
comments/4comments.controller.ts comments.module.ts comments.service.ts dto.ts
databases/9
__tests__/3normalize-select-options.test.ts option-id-canonicalization.test.ts record-open-query-shape.test.ts
database.service.ts databases.controller.ts databases.module.ts dto.ts property-cache-schema.ts query.service.ts
docs/2docs.controller.ts docs.module.ts
files/10
__tests__/1quota.test.ts
dto.ts files.controller.ts files.module.ts files.service.ts quota.ts record.ts s3.ts uploads.controller.ts uploads.service.ts
forms/18debug.controller.ts form.dto.ts forms.controller.ts forms.module.ts forms.service.ts markdown.test.ts markdown.ts optional-auth.guard.ts public-forms.controller.ts public-forms.service.ts question.dto.ts questions.service.ts rate-limit.ts recaptcha.ts slug.ts submissions.controller.ts submissions.service.ts submit-schema.ts
health/3health.controller.ts health.module.ts health.service.ts
integrations/3integrations.controller.ts integrations.module.ts integrations.service.ts
internal/12
__tests__/2flush-query-shape.test.ts internal.endpoints.test.ts
ai-test-seam.controller.ts ai-test-seam.dto.ts ai-test-seam.service.ts internal.controller.ts internal.dto.ts internal.module.ts internal.service.ts test-seam.controller.ts test-seam.dto.ts test-seam.service.ts
notifications/3notifications.controller.ts notifications.module.ts notifications.service.ts
oauth/6
__tests__/1oauth.scopes.test.ts
oauth.controller.ts oauth.module.ts oauth.service.ts scopes.ts token-keys.ts
org/15audit.service.ts embed-preview.controller.ts guards.ts members.service.ts org-admin.controller.ts org-policy.service.ts org.module.ts overview.service.ts policy-effective.controller.ts policy.schema.ts policy.test.ts retention.service.ts totp.test.ts totp.ts two-factor.service.ts
orgs/5dto.ts orgs.controller.ts orgs.module.ts orgs.service.ts util.ts
pages/4dto.ts page.service.ts pages.controller.ts pages.module.ts
scim/15
__tests__/3email-mapping.test.ts etag.test.ts filters.test.ts
etag.ts filters.ts groups.service.ts schemas.ts scim-admin.controller.ts scim-admin.service.ts scim-auth.guard.ts scim-auth.service.ts scim-errors.ts scim.controller.ts scim.module.ts users.service.ts
search/4
__tests__/1search.service.test.ts
search.controller.ts search.module.ts search.service.ts
sessions/3sessions.controller.ts sessions.module.ts sessions.service.ts
share/3share.controller.ts share.module.ts share.service.ts
sso/16
__tests__/4enforcement.test.ts oidc.verify.test.ts provisioning.test.ts saml.validate.test.ts
enforcement.ts oidc.controller.ts oidc.service.ts oidc.ts provisioning.service.ts saml.controller.ts saml.ts schemas.ts sso-admin.controller.ts sso-admin.service.ts sso-secrets.ts sso.module.ts
teams/4dto.ts teams.controller.ts teams.module.ts teams.service.ts
users/4dto.ts users.controller.ts users.module.ts users.service.ts
webhooks/3webhooks.controller.ts webhooks.module.ts webhooks.service.ts
workspaces/4dto.ts workspaces.controller.ts workspaces.module.ts workspaces.service.ts
types/1xmldom.d.ts
app.module.ts constants.ts main.ts
_probe_rawbody.cjs .env.example Dockerfile package.json README.md tsconfig.json vitest.config.ts
realtime/33
src/17auth.ts comments.ts config.ts event-source.ts http-store.ts index.ts metrics.ts notifications.ts page-updated-source.ts presence.ts prisma-store.ts protocol.ts rate-limit.ts room-key.ts rooms.ts server.ts update-log.ts
test/11auth.test.ts config.test.ts event-source.test.ts http-store.test.ts metrics-merge.test.ts page-updated-source.test.ts presence.test.ts protocol.test.ts rate-limit.test.ts rooms.test.ts task-0094-builders.test.ts
.env.example Dockerfile package.json README.md tsconfig.json
web/255
src/244
app/45
__tests__/1theme-tokens.test.ts
(app)/26
w/25
[workspace]/25
ask/1page.tsx
databases/1
[id]/1page.tsx
favorites/1page.tsx
files/1page.tsx
notifications/1page.tsx
page/4
[id]/4
[block]/1page.tsx
share/1page.tsx
layout.tsx page.tsx
search/1page.tsx
settings/10
appearance/1page.tsx
automations/3
[id]/1page.tsx
new/1page.tsx
page.tsx
billing/1page.tsx
developers/1page.tsx
members/1page.tsx
permissions/1page.tsx
teams/1page.tsx
page.tsx
shared/1page.tsx
templates/1page.tsx
trash/1page.tsx
layout.tsx page.tsx
layout.tsx
(auth)/4
login/1page.tsx
register/1page.tsx
reset-password/1page.tsx
layout.tsx
api/2
metrics/1route.ts
v1/1
health/1route.ts
f/1
[slug]/1page.tsx
health/1
ready/1route.ts
org/2
[orgId]/2
admin/2
integrations/1page.tsx
page.tsx
share/1
[id]/1page.tsx
error.tsx global-error.tsx globals.css layout.tsx not-found.tsx page.tsx providers.tsx
components/118
admin/10
org-admin/8ApiKeysTab.tsx AuditTab.tsx MembersTab.tsx OverviewTab.tsx PoliciesTab.tsx RetentionTab.tsx WebhooksTab.tsx WorkspacePicker.tsx
ScimCard.tsx SsoTabs.tsx
ai/6ai-panel.tsx ask-ai-submenu.tsx db-ai-dialog.tsx generate-page-dialog.tsx writing-actions-menu.tsx writing-diff.tsx
automations/5action-editor.tsx condition-editor.tsx lib.ts run-history.tsx trigger-editor.tsx
collaboration/6CursorLayer.tsx OfflineBanner.tsx PresenceBar.tsx README.md ReviewFailedDialog.tsx StatusChip.tsx
comments/3comment-panel.tsx comment-thread.tsx mention-input.tsx
database/27
__tests__/2cell-editor.test.tsx cell-renderer.test.tsx
AiExtractDialog.tsx AiFormulaDialog.tsx AiGenerateDialog.tsx board-view.tsx buttons-panel.tsx calendar-view.tsx cell-editor.tsx cell-renderer.tsx chart-view.tsx database-page-view.tsx database-toolbar.tsx database-view-renderer.tsx db-embedded.tsx filter-builder.tsx form-builder-view.tsx formula-editor.tsx gallery-view.tsx group-builder.tsx list-view.tsx property-editor.tsx record-header.tsx record-panel.tsx sort-builder.tsx table-view.tsx timeline-view.tsx
dialogs/7
__tests__/1move-to-dialog.test.tsx
create-dialog.tsx dialog.tsx file-permissions-dialog.tsx invite-dialog.tsx move-to-dialog.tsx share-dialog.tsx
editor/19
__tests__/3code-block-text.test.ts code-block.test.tsx expandable.test.tsx
block-card.tsx block-renderer.tsx block-save-queue.ts block-toolbar.tsx context-menu.tsx expandable.tsx history-popover.tsx inline-editor.tsx insert-dialog.tsx page-canvas.tsx page-spinner.tsx page-view.tsx selection-toolbar.tsx slash-menu.tsx template-dialog.tsx type-menu.tsx
files/4FileBlock.tsx FileBrowser.tsx FilePicker.tsx PreviewModal.tsx
form/4form-field.tsx form-renderer.tsx form-responses-view.tsx form-view.tsx
layout/8
__tests__/1page-tree-icon.test.tsx
command-palette.tsx factory-logo.tsx page-tree-node.tsx sidebar.tsx theme-provider.tsx topbar.tsx workspace-shell.tsx
mentions/3
__tests__/1mention-model.test.ts
mention-model.ts MentionPopup.tsx
search/3ask-ai-search.tsx results-list.tsx search-dialog.tsx
settings/10
api-keys/4ApiKeyCreateDialog.tsx ApiKeyRevealDialog.tsx ApiKeyTable.tsx ScopesBadge.tsx
webhooks/5CurlExamples.tsx DeliveryInspector.tsx EventExplorer.tsx OverviewTab.tsx WebhooksTab.tsx
settings-shell.tsx
ui/3confirm-dialog.tsx form.tsx toaster.tsx
hooks/21
__tests__/4realtime-frame-handler.test.ts use-blocks.test.ts use-database-options.test.ts use-workspace.test.tsx
realtime-frame-handler.ts use-ai-stream.ts use-auth.ts use-blocks.ts use-database.ts use-db-ai-stream.ts use-forms.ts use-members.ts use-mention-sources.ts use-notifications.ts use-offline-status.ts use-pages.ts use-presence-announcer.ts use-presence-toasts.ts use-presence.ts use-realtime.ts use-workspace.ts
lib/50
__tests__/21ai-database.test.ts ai-generate.test.ts ai-sse.test.ts ai-writing.test.ts api-keys.test.ts api-members.test.ts auth-refresh.test.ts auth-store.test.ts batch-queue-drop.test.ts block-content.test.ts clipboard-import.test.ts code-highlight.test.ts offline-status.test.ts presence-announcer.test.ts presence-color.test.ts presence-cursor.test.ts presence-emitter.test.ts realtime-url.test.ts search.test.ts utils.test.ts writing-diff.test.ts
files/7
__tests__/2picker.test.ts upload.test.ts
api.ts picker.ts upload-store.ts upload.ts use-file-uploads.ts
ai-database.ts ai.ts api-keys.ts api.ts auth-store.ts batch-queue.ts block-content.ts clipboard-import.ts code-highlight.ts constants.ts offline-status.ts org-admin.ts presence-announcer.ts presence-color.ts presence-cursor.ts presence-editor-views.ts presence-emitter.ts query-client.ts realtime.ts search.ts types.ts utils.ts
stores/8
__tests__/1block-patch-store.test.ts
block-patch-store.ts comment-pill-store.ts database-store.ts editor-store.ts offlineQueue.ts presence-store.ts ui-store.ts
instrumentation.ts middleware.ts
.env.example Dockerfile next-env.d.ts next.config.mjs package.json postcss.config.mjs README.md tailwind.config.ts tsconfig.json tsconfig.tsbuildinfo vitest.config.ts
worker/92
src/87
__tests__/13ai-embedding.test.ts ai-request.test.ts ai-usage-budget.test.ts audit-export.test.ts email-decision.test.ts encrypt.test.ts health-search-index-lag.test.ts mime.test.ts retention.test.ts s3-sign.test.ts search-index.test.ts smtp.test.ts webhook-sign.test.ts
health/1server.ts
lib/12
__tests__/1clamav.test.ts
clamav.ts encrypt.ts job-otel.ts mime.ts notify.ts prisma.ts redis-stream.ts redis.ts s3.ts sharp.ts smtp.ts
processors/56
automations/28
__tests__/3condition-evaluator.spec.ts executors.spec.ts run-pipeline.spec.ts
executors/17assign-user.ts call-integration.ts change-status.ts classify.ts create-notification.ts create-page.ts email-cap.ts helpers.ts integration-registry.ts permission.ts record-write.ts registry.ts run-ai-action.ts send-email.ts send-webhook.ts types.ts update-record.ts
condition-evaluator.ts dead-letter.ts log.ts matcher.ts pipeline.ts run-state.ts schedule.ts watchdog.ts
files/15
__tests__/4errors.test.ts scan-gate.test.ts status.test.ts temp.test.ts
steps/4finalize.ts metadata.ts scan.ts thumbnails.ts
cleanup.ts errors.ts generateThumbs.ts processor.ts status.ts temp.ts types.ts
search-index/4builders.ts grants.ts index.ts processor.ts
ai-embedding.ts ai.processor.ts audit-export.ts automations.processor.ts email-decision.processor.ts email.processor.ts maintenance.processor.ts retention.ts webhooks.processor.ts
config.ts events.ts index.ts logger.ts queues.ts
.env.example Dockerfile package.json README.md tsconfig.json
docs/73
api/17ai.md authentication.md automations.md blocks.md comments.md databases.md files.md forms.md openapi.yaml org-admin.md pages.md scim.md search.md sso.md versioning.md webhooks.md workspaces.md
architecture/10ai-architecture.md automation.md backend-architecture.md database-architecture.md frontend-architecture.md realtime-architecture.md search-architecture.md security-architecture.md storage.md system-architecture.md
database/2database-architecture.md redis-keys.md
development/8admin-guide.md coding-standards.md deployment.md development-guide.md files-pipeline.md perf-report.md performance.md testing.md
product/4functional-requirements.md non-functional-requirements.md product-requirements.md ux-requirements.md
requirements/1requirements-matrix.md
security/5audit-report.md files.md forms.md license-allowlist.md security-architecture.md
tasks/16phase-00-foundation.md phase-01-authentication.md phase-02-workspace.md phase-03-pages.md phase-04-editor.md phase-05-databases.md phase-06-relations.md phase-07-collaboration.md phase-08-search.md phase-09-files.md phase-10-forms.md phase-11-automation.md phase-12-ai.md phase-13-api.md phase-14-enterprise.md phase-15-production.md
ui/9ai.md automations.md design-system.md developers.md files.md forms.md org-admin.md scim.md sso.md
support-matrix.md
infrastructure/71
caddy/1Caddyfile
docker/6
init/101-extensions.sql
opensearch/1init-indices.json
.env.docker docker-compose.yml opensearch-init.sh README.md
kubernetes/40
observability/20
alertmanager/1alertmanager.yaml
grafana/8
dashboards/6ai.json api-overview.json infra.json realtime.json search.json workers-queues.json
provisioning/2
dashboards/1dashboards.yaml
datasources/1datasource.yaml
prometheus/3alerts.yaml recording-rules.yaml scrape.yaml
scripts/1sync-sources.mjs
alertmanager.yaml grafana.yaml kustomization.yaml namespace.yaml node-exporter.yaml otel-collector.yaml prometheus.yaml
postgres/7configmap.yaml cronjob-backup.yaml cronjob-opensearch-snapshot.yaml pgbouncer.yaml replica.yaml statefulset.yaml walg-iam-policy.yaml
api.yaml configmap.yaml ingress.yaml kustomization.yaml namespace.yaml networkpolicy.yaml pdb.yaml README.md realtime.yaml redis.yaml secret.example.yaml web.yaml worker.yaml
nginx/2nginx.conf README.md
observability/14
alertmanager/1alertmanager.yaml
grafana/8
dashboards/6ai.json api-overview.json infra.json realtime.json search.json workers-queues.json
provisioning/2
dashboards/1dashboards.yaml
datasources/1datasource.yaml
prometheus/3alerts.yaml recording-rules.yaml scrape.yaml
scripts/1dashboard-completeness.mjs
README.md
terraform/8compute.tf data.tf main.tf network.tf outputs.tf README.md security.tf variables.tf
packages/289
ai/24
src/20
providers/7anthropic.ts google.ts http.ts index.ts openai-compatible.ts rag.test.ts writing.test.ts
schemas/2database.ts generate.ts
adapters.test.ts config.test.ts config.ts gateway.test.ts gateway.ts index.ts tools.test.ts tools.ts types.ts usage.test.ts usage.ts
package.json README.md tsconfig.json vitest.config.ts
auth/11
src/7index.ts passwords.test.ts passwords.ts sessions.test.ts sessions.ts tokens.test.ts tokens.ts
package.json README.md tsconfig.json vitest.config.ts
collaboration/17
src/13awareness.ts codec.test.ts codec.ts index.ts lww.test.ts lww.ts mapping.ts offline.ts outbox.test.ts outbox.ts provider.test.ts provider.ts types.ts
package.json README.md tsconfig.json vitest.config.ts
config/7
src/4eslint.base.js index.ts prettier.base.json tsconfig.base.json
package.json README.md tsconfig.json
database-client/5
src/2index.test.ts index.ts
package.json README.md tsconfig.json
database-engine/33
src/29
filter/2filter.test.ts index.ts
formula/8evaluator.ts formula.test.ts functions.ts index.ts lexer.ts parser.ts types.ts validate.ts
query/6group.ts index.ts paginate.ts pivot.ts query.test.ts sort.ts
relations/2index.ts relations.test.ts
rollups/2index.ts rollups.test.ts
values/5dates.ts index.ts registry.ts types.ts values.test.ts
views/3buttons.ts index.ts views.test.ts
index.ts
package.json README.md tsconfig.json vitest.config.ts
editor/25
src/21
marks/1highlight.ts
nodes/2equation.ts mention.ts
autosave.test.ts autosave.ts clipboard.test.ts clipboard.ts codec.test.ts codec.ts context-menu.test.ts context-menu.ts diff.test.ts diff.ts factory.ts history.test.ts history.ts index.ts positions.ts slash.test.ts slash.ts types.ts
package.json README.md tsconfig.json vitest.config.ts
observability/20
src/16
__tests__/8db-redis-metrics.test.ts metrics.test.ts otel.test.ts pino-otel.test.ts prometheus.test.ts resource.test.ts scrubber.test.ts traceparent.test.ts
index.ts metrics.ts otel.ts pino-otel.ts prometheus.ts resource.ts scrubber.ts traceparent.ts
package.json README.md tsconfig.json vitest.config.ts
permissions/11
src/7index.ts levels.test.ts levels.ts resolve.test.ts resolve.ts rules.test.ts rules.ts
package.json README.md tsconfig.json vitest.config.ts
search/22
src/18chunking.test.ts chunking.ts client.test.ts client.ts embeddings.test.ts embeddings.ts hybrid.test.ts hybrid.ts index.ts indexer.test.ts indexer.ts mapping.test.ts mapping.ts queries.test.ts queries.ts rrf.test.ts rrf.ts types.ts
package.json README.md tsconfig.json vitest.config.ts
shared/61
src/57
dto/17ai.dto.ts auth.dto.test.ts auth.dto.ts block.dto.ts browser-safety.test.ts comment.dto.ts database.dto.test.ts database.dto.ts file.dto.ts formula-ast.ts index.ts notification.dto.ts page.dto.ts search.dto.ts share.dto.ts webhook.dto.ts workspace.dto.ts
events/8audit-catalog.test.ts audit-catalog.ts audit-csv.test.ts audit-csv.ts audit-scope.test.ts audit-scope.ts catalog.test.ts catalog.ts
realtime/6comment-events.ts frames.test.ts frames.ts notification-policy.ts notification-prefs.ts task-0094.test.ts
automation.test.ts automation.ts constants.ts crypto.test.ts crypto.ts env.ts errors.ts events.test.ts events.ts forms.test.ts forms.ts index.ts markdown.test.ts markdown.ts mentions.test.ts mentions.ts net.test.ts net.ts positions.test.ts positions.ts presence.test.ts presence.ts security-headers.ts webhook-delivery.test.ts webhook-delivery.ts webhook-events.ts
package.json README.md tsconfig.json vitest.config.ts
storage/16
src/12client.test.ts client.ts index.ts keys.test.ts keys.ts mime.test.ts mime.ts presign.test.ts presign.ts thumbnails.test.ts thumbnails.ts types.ts
package.json README.md tsconfig.json vitest.config.ts
ui/37
src/33
components/19avatar.tsx badge.tsx button.tsx checkbox.tsx command.tsx context-menu.tsx dialog.tsx dropdown-menu.tsx empty-state.tsx index.ts popover.tsx separator.tsx skeleton.tsx slider.tsx spinner.tsx switch.tsx tabs.tsx toast.tsx tooltip.tsx
color.ts command-filter.test.ts command-filter.ts focus.test.ts focus.ts formatters.test.ts formatters.ts index.ts theme.test.ts theme.ts toast-store.test.ts toast-store.ts tokens.test.ts tokens.ts
package.json README.md tsconfig.json vitest.config.ts
prisma/16
migrations/13
20260927000000_init/1migration.sql
20260928000000_search_documents_content_hash/1migration.sql
20260929000000_workspace_usage_storage_bytes/1migration.sql
20260930000000_form_submitted_notification_type/1migration.sql
20261001000000_ai_phase12/1migration.sql
20261002000000_webhooks_secret_encrypted_dlq/1migration.sql
20261002111436_webhook_delivery_latency/1migration.sql
20261002120000_org_admin_2fa_retention_unique/1migration.sql
20261002215124_audit_export/1migration.sql
20261003210000_perf_indexes_hot_paths/1migration.sql
20261004004500_perf_indexes_hot_paths_2/1migration.sql
migration_lock.toml README.md
schema.prisma seed.ts verify.ts
scripts/7backup-health.ts docs-completeness.mjs generate-openapi.mjs minio-init.ts opensearch-reindex.ts storage-orphans.ts verify-clean-clone.sh
tests/169
e2e/63
.auth/1user.json
fixtures/7
files/6clip.mp4 clip.wav clip.webm hero-1400.png note.docx note.txt
realtime.ts
mocks/2oidc-issuer.ts saml-idp.ts
playwright-report/3
data/2a6557bae9c2714a658968d4eb8534195c08ebda8.png e3092d07d00a16063e46385c02a559a9054fa847.webm
index.html
reports/1files-security.json
specs/34ai-database.spec.ts ai-generate.spec.ts ai-rag.spec.ts ai-security.spec.ts ai-writing.spec.ts ai.spec.ts api-keys.spec.ts appearance-theme.spec.ts auth.spec.ts automation.spec.ts collab-two-tab.spec.ts comments.spec.ts databases.spec.ts default-workspace-alias.spec.ts developers.spec.ts editor.spec.ts files.spec.ts final-flow.spec.ts forms.spec.ts oauth.spec.ts org-admin.spec.ts pages.spec.ts performance.spec.ts presence-cursors.spec.ts qa-paste-toggle.spec.ts realtime.spec.ts scim.spec.ts search-index-lag.spec.ts search.spec.ts sharing.spec.ts sso.spec.ts task-0094-comments-notifications.spec.ts webhooks.spec.ts workspace.spec.ts
test-results-final-flow/3
final-flow-Release-v1-0-fi-6c06b-w-state-carries-sequential--chromium-desktop/2test-failed-1.png trace.zip
.last-run.json
utils/4api.ts dnd.ts search-lag.ts wait.ts
.gitignore fixtures.ts global-setup.ts package.json playwright.config.ts playwright.final-flow.config.ts README.md tsconfig.json
fixtures/1
files/1make-sparse.ts
integration/60
ai-capability/2ai-app.capability.test.ts gateway.capability.test.ts
drill/2seed-drill.mjs verify-restore.mjs
fixtures/5
files/5clip.mp4 clip.wav doc.pdf hero-1400.png note.txt
helpers/3api.ts containers.ts seed.ts
realtime/11control-channel.test.ts convergence.test.ts flush-rebase-http.test.ts handshake.test.ts heartbeat-limits.test.ts lifecycle.test.ts presence.test.ts rebase.test.ts resume.test.ts rt-helpers.ts task-0094-fanout.test.ts
ai-chat.integration.test.ts ai-database.integration.test.ts ai-generate.integration.test.ts ai-rag.integration.test.ts ai-writing.integration.test.ts api-keys.integration.test.ts audit-completeness.integration.test.ts audit-export-s3.integration.test.ts auth.integration.test.ts automation-pipeline.integration.test.ts automations.integration.test.ts blocks.integration.test.ts cache-invalidation.integration.test.ts comments.integration.test.ts databases.integration.test.ts files-lifecycle.integration.test.ts files-pipeline.integration.test.ts files-security.integration.test.ts files.integration.test.ts forms-responses.integration.test.ts forms.integration.test.ts oauth.integration.test.ts openapi.contract.test.ts org-admin.integration.test.ts package.json pages.integration.test.ts permissions.integration.test.ts README.md replica-routing.integration.test.ts retention-engine.integration.test.ts scim.integration.test.ts search.integration.test.ts sso.integration.test.ts tsconfig.json vitest.config.ts webhooks.integration.test.ts workspace.integration.test.ts
performance/28
fixtures/1search-100k.ts
helpers/2seed-100k.mjs seed-1k-load.mjs
reports/7api-load-smoke.json database-load-smoke.json editor-local.json realtime-load.json search-full.json search-last.json search-reduced.json
api-load.js api.js archive.tar database-load.js database.js editor-local.mjs extract-100k-env.mjs package.json README.md realtime-1k.js realtime-load.js realtime.js run-realtime-load.sh search-search.k6.js search-summary.json seed-100k.mjs threshold-breach-demo.js verify-zero-op-loss.mjs
security/16
helpers/1app.ts
audit-report.test.ts auth-paths.test.ts deps.test.ts formula-fuzz.test.ts gitleaks-config.test.ts headers.test.ts images.test.ts oracle.ts package.json permission-fuzz.test.ts rate-limits.test.ts README.md tsconfig.json upload-abuse.test.ts vitest.config.ts
README.md
.dockerignore .env .env.bak-task0125 .env.example .eslintrc.json .gitignore .npmrc .prettierignore .prettierrc.json AGENTS.md ARCHITECTURE.md CHANGELOG.md CONTRIBUTING.md gitleaks.toml LICENSE package.json pnpm-lock.yaml pnpm-workspace.yaml PRODUCT-SPEC.md README.md ROADMAP.md SECURITY.md tsconfig.json turbo.json
phase-09-files.mddocs/tasks/phase-09-files.md← tasks105,728 B← Back to the app

Phase 9 — Files (Task Specs)

Purpose

Phase 9 delivers file management end to end: S3-compatible storage wired through presigned upload/download, a worker file pipeline (virus scan → metadata → thumbnails), file blocks and previews in the editor, plus versions, permissions, quotas, trash, and the security guarantees (malware blocking, MIME sniffing, path sanitization) the platform's security model requires.

Phase Exit Criteria (from ROADMAP.md § Phase 9)

Tasks

ID Title Estimate
TASK-0101 Storage integration: presigned upload/download, MIME validation, key layout L
TASK-0102 File pipeline worker: scan, metadata, thumbnails, status machine L
TASK-0103 File API + upload UX + file block rendering L
TASK-0104 File management: versions, permissions, quota, trash M
TASK-0105 Files E2E + security suite M

Phase 9 Notes — recorded decisions & conflicts (AGENTS.md rule 29)

The authoritative record is docs/architecture/storage.md § 0 (Implementation Notes).

Phase Completion Checklist


TASK-0101

Title

Storage integration: presigned upload/download sessions, MIME magic-byte validation, workspace key layout, signed GET URLs

Objective

Wire packages/storage into the API files module and the worker so that uploads happen as plan-limited presigned PUT sessions with magic-byte MIME verification, objects land in a private per-workspace key layout, and downloads are served via short-lived (15 min) signed GET URLs behind a server-side permission check.

Background

packages/storage scaffolds an S3 client (src/client.ts), a key layout helper (src/keys.ts), MIME magic-byte validation (src/mime.ts), and a thumbnail interface (src/thumbnails.ts). MinIO is provisioned by Phase 0 (Docker) and is S3-compatible. The Prisma File model exists (prisma/schema.prisma: files — workspaceId, blockId?, pageId?, name, key @unique, url?, size Int, mimeType, status FileStatus @default(UPLOADING), metadata Json, thumbnailKey/Url, scanReport Json?, uploadedBy, permissions Json?, deletedAt; plus FileVersion file_versions). Size limits: free plan 50 MB per file, paid 500 MB per file — sourced from Workspace.usageLimits (Json; default shape { maxFileSize: 52428800, storageQuota: 1073741824 } for free, { maxFileSize: 524288000, storageQuota: 10737418240 } for paid; the effective limit = min(usageLimits.maxFileSize, plan default)). LIMITS.MAX_FILE_SIZE = 500 MB is the hard ceiling (packages/shared/src/constants.ts). The files module scaffold is apps/api/src/modules/files/.

Requirements

  1. Storage config: @factory/config env — S3_ENDPOINT, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET_FILES (private bucket), S3_BUCKET_THUMBS (read-only serving bucket, still server-authorized), S3_SIGN_EXPIRY_SECONDS=900, CLAMD_SOCKET (optional). Bucket policy: no public access; both buckets Private ACL.
  2. Key layout (packages/storage/src/keys.ts): files/{workspaceId}/{fileId}/{version}/{safeName} and thumbs/{workspaceId}/{fileId}/{size}/{safeName}. safeName = original basename with all path components stripped (path.basename, then replace [\x00-\x1f\x7f<>:"\\/*?|] with _, max 180 chars, extension preserved case-insensitively); collisions get a -<8 hex> suffix. Bucket naming per deployment: one bucket per environment, all workspaces shared inside it (workspace isolation is by key prefix + permission checks, NOT by bucket ACL — document this decision).
  3. Upload sessions: POST /api/v1/files/uploads { name, size, mimeType, pageId?, blockId?, replaceFileId? } → 201 { upload: { file: FileRecord (status UPLOADING), uploadUrl, method: "PUT", headers: { "Content-Type": <validated mime>, "Content-Length": size }, expiresAt } }. Server: authenticates + checks EDIT on the target page/block (or EDIT on workspace root for unattached), enforces size ≤ effective maxFileSize (413 FILE_TOO_LARGE with the limit in the message body) and size ≤ remaining storageQuota (413 QUOTA_EXCEEDED), creates the File row, and signs a PUT URL (15 min, content-length-locked when the S3 SDK supports it).
  4. MIME validation: the client-claimed mimeType is checked against the allow-list (images: png/jpg/webp/gif/svg; documents: pdf/docx/xlsx/pptx/ md/txt/csv; archives: zip; video: mp4/webm/mov; audio: mp3/wav/m4a; other: application/octet-stream) via packages/storage/src/mime.ts; SVG uploads are restricted to text/plain-treated (served with Content-Disposition: attachment + X-Content-Type-Options: nosniff — see Security). On PUT /api/v1/files/uploads/{uploadId}/complete the server HEADs the object, then downloads the first 8 KiB and runs sniffMagicBytes(buffer) from packages/storage/src/mime.ts: mismatch with the claimed type → status FAILED, error MIME_MISMATCH, object deleted, session closed (integration test in TASK-0105).
  5. Signed GET: GET /api/v1/files/{fileId} (metadata, includes downloadUrl + previewUrl when available, 15-min signed URLs) and GET /api/v1/files/{fileId}/download (302 → signed URL) both require the caller to have ≥ VIEW on the file (TASK-0104 defines file permission resolution; here: ≥ VIEW on the containing page/block, or explicit file grant). URLs are signed for exactly one object key, expiry 900 s, and include response-content-disposition (attachment for download, inline for preview of image/video/audio/pdf).
  6. Upload completion: PUT .../complete verifies object existence + size matches the session (422 UPLOAD_INCOMPLETE otherwise), sets File.status = SCANNING, enqueues files queue job process-file { fileId, version: 1 }, and closes the session. POST .../cancel deletes the partial object + row (status FAILED, deletedAt set).
  7. Concurrency: one active upload session per (file, version); duplicate sessions for the same replaceFileId are allowed but each is an independent version (see TASK-0104).

Acceptance Criteria

Backend Changes

Frontend Changes

None in this task (upload UI is TASK-0103).

Database Changes

API Changes

Tests

Security

Documentation

Dependencies

Definition of Done


TASK-0102

Title

File pipeline worker: status machine, ClamAV scan, metadata extraction, thumbnails, file.uploaded event

Objective

Complete the files queue processor so every uploaded object transitions UPLOADING → SCANNING → READY | VIRUS_DETECTED | FAILED with a ClamAV scan, extracted metadata (mime, size, sha256, image dimensions / video duration / PDF pages), and generated thumbnails (images in 3 sizes, PDF first page, video first frame) — emitting file.uploaded and updating the search index on success.

Background

The upload session (TASK-0101) leaves the File row in SCANNING and enqueues process-file on the BullMQ files queue (apps/worker/src/queues.ts). Prisma FileStatus enum: UPLOADING, SCANNING, READY, VIRUS_DETECTED, FAILED; File.metadata (Json) holds dimensions / duration / pages / hashes; File.scanReport holds the ClamAV result; FileVersion records each stored generation. packages/storage/src/ provides thumbnails.ts (interface) and the S3 client. sharp (images), pdfjs-dist or mupdf (PDF rasterization), fluent-ffmpeg (video first frame) are the candidate libraries — each must be justified per AGENTS.md rule 17 and pinned in apps/worker/package.json. Domain events: file.uploaded in @factory/shared/events. The search indexer (TASK-0096) consumes file.uploaded to index file docs.

Requirements

  1. Processor: apps/worker/src/processors/files/processor.ts with job process-file { fileId, version }; at-least-once safe (re-entrant: re-running a READY file's job is a no-op; re-running after FAILED is an explicit retry). Steps run sequentially, each wrapped with timing + structured logs (file:{fileId}:step:{name}), and each step's failure classifies as retryable (network, S3 5xx) or fatal (corrupt object, unsupported format) → retryable: BullMQ backoff; fatal: status FAILED + error recorded in metadata.pipeline.error.
  2. Status machine (only valid transitions, enforced in the processor): UPLOADING → SCANNING (set at complete, TASK-0101) → READY | VIRUS_DETECTED | FAILED. READY → FAILED only on later re-scan. Any other transition throws and is logged as a pipeline invariant violation.
  3. Virus scan: if CLAMD_SOCKET is configured, run clamdscan over the local temp copy (download to temp, scan, delete temp — object never stays on disk > scan duration + 5 min cleanup cron); INFECTED → status VIRUS_DETECTED, scanReport = { engine: "clamav", version, infected: true, signature }, NO thumbnail generation, NO search index update, file.uploaded NOT emitted; emit domain event file.virus_detected (add to @factory/shared/events) and notification to the workspace admin (via Phase-7 notification service, stub call if module absent). If CLAMD_SOCKET is unset: dev mode — log WARN file scan skipped (CLAMD_SOCKET not configured) and continue, scanReport = { engine: "none", skipped: true, reason }; in NODE_ENV=production a missing socket is a boot error (worker refuses to start — fail closed in prod, documented in the runbook).
  4. Metadata extraction (streaming where possible): mime (re-sniff from bytes, final authority), size (S3 head), sha256 (streaming hash, metadata.sha256), images: width/height/orientation via sharp (metadata.dimensions); video: duration seconds + codec via ffprobe (metadata.media); PDF: page count via pdfjs-dist (metadata.pdf = { pages }). Unsupported/undecodable → keep mime as declared, metadata.pipeline.warnings[], still READY (a file is downloadable even without rich metadata — only thumbnails are affected).
  5. Thumbnails (write to thumbs/ key layout via packages/storage/src/thumbnails.ts): images → 3 sizes (160, 480, 1280 px wide, same format, max 1280) at keys thumbs/{ws}/{fileId}/{160|480|1280}.{ext}; PDF → first page PNG (1280w)
    • page count; video → first frame PNG (1280w); audio/other → no thumbnail (thumbnailKey null). File.thumbnailKey = the 480 variant (primary UI thumb); File.thumbnailUrl = signed 15-min GET (regenerated on read, never stored long-lived).
  6. On success: status READY, metadata.pipeline = { scannedAt, sha256, durationMs, steps: [...] }, emit file.uploaded domain event { workspaceId, fileId, name, mimeType, size, blockId?, pageId? } → search indexer indexes the file doc (TASK-0096); record FileVersion { version, key, size, createdBy } (created by TASK-0101 on complete; here only verify + backfill on re-runs).
  7. Job tuning: concurrency 4 (env FILES_WORKER_CONCURRENCY), removeOnComplete: 500, removeOnFail: 200, attempt limit 3 with exponential backoff (2 s base), temp-file cleanup cron every 5 min (delete files older than 10 min in the temp dir), disk guard: abort with fatal error if temp dir free space < 512 MB.

Acceptance Criteria

Backend Changes

Frontend Changes

None (worker only; rendering is TASK-0103).

Database Changes

API Changes

Tests

Security

Documentation

Dependencies

Definition of Done


TASK-0103

Title

File API + upload UX (drag-drop, paste, block) + file block rendering + file picker + file browser

Objective

Let users put files into pages: drag-drop from the OS, paste an image, or insert via the file block button — with upload progress — and render every file kind correctly (image inline + resizable + caption, native video/audio players, PDF preview, document download card), plus a file picker in the page sidebar and a file browser.

Background

Upload sessions (TASK-0101) and the pipeline (TASK-0102) exist: the client gets a presigned uploadUrl, PUTs the blob, calls complete, then polls / subscribes for status. The editor (Phase 4) has block types incl. file (BlockType enum in Prisma) and the slash menu; packages/editor holds the block model. Signed URLs (15 min) come from GET /api/v1/files/:id and .../preview. The file picker and browser are plain Next.js pages/components under apps/web/. File.metadata carries dimensions, media, pdf, sha256, pipeline.

Requirements

  1. Upload paths (all → one uploadFile(file, { pageId, blockId?, replaceFileId? }) service in apps/web/src/lib/files/upload.ts): (a) drag-drop from OS onto the page canvas → drop indicator, inserts a file block at the drop position; (b) paste (Ctrl+V) an image from the clipboard → image block at the cursor; (c) file block "Add file" button → native file input (multi). Each path: size pre-check against the plan limit (fetched once per page session) with a clear error toast ("50 MB per file on your plan"), then presigned session → XHR PUT (not fetch: progress events) with a progress bar on the block (0–100 %, speed, cancel button) → complete → status polling (2 s, max 60 s) → block switches to rendering.
  2. Upload failure UX: presigned PUT 403 (expired) → auto-retry once with a fresh session; MIME mismatch / quota / virus → error state on the block with the server message + "Remove" and (for recoverable quota) "Upgrade" hint; cancel mid-upload → cancel API + block removed (or "Upload failed — retry" if the user prefers; default: remove).
  3. File block rendering (apps/web/src/components/blocks/FileBlock.tsx): image: inline, max-width 100 %, resizable via bottom-right drag handle (50/75/100 % presets + free drag, stored on the block as widthPercent), caption toggle below (inline editable text); video: native <video controls> (poster = thumbnail); audio: native <audio controls> + icon; PDF: preview mode — iframe with the signed preview URL (15-min refresh on expiry: re-fetch before use when now + 60 s > expiresAt) + "Open in new tab" + "Download"; document/other: download card — file-type icon (by mime), name, size (human), version count (if > 1), Download button (302 endpoint), "Copy link" (share link = GET /files/:id — public only if the page is shared; otherwise "link requires access" tooltip). While status ≠ READY: skeleton with status label (Scanning…, Processing thumbnails…); VIRUS_DETECTED: red card "Blocked by virus scan — contact an admin" (no download affordance).
  4. File picker (page sidebar): "Files" tab listing files attached to this page tree (workspace-scoped filter chip: "In this page / In workspace"), grid (thumbnail, name, size, uploader, date), click → insert as file block at cursor; search box (client filter by name); empty state ("No files here yet — drag one in").
  5. File browser: route apps/web/src/app/(workspace)/files/page.tsx — table of workspace files (name, kind icon, size, uploader, page, status, updated), sort (name/size/date/updated), filter by kind (image/video/audio/pdf/doc/other) + status, row click → preview modal (image lightbox / pdf iframe / video player / download card) + "Open page" link; pagination 50/page (cursor); empty + error states; mobile: card list instead of table.
  6. Permissions in UI: items the user lacks VIEW on are absent (server filters); a file in a private page the user can't open renders "No access" card if reached via stale URL.
  7. Accessibility: upload progress announced via aria-live="polite" (25 % steps), cancel is a real button, video/audio players have text alternatives (name + duration), picker grid keyboard-navigable (roving tabindex), download card is a <button> not a styled div.

Acceptance Criteria

Backend Changes

Frontend Changes

Database Changes

API Changes

Tests

Security

Documentation

Dependencies

Definition of Done



TASK-0104

Title

File management: versions, permission-gated download + audit, trash/restore/purge, storage quota, file-level permission overrides

Objective

Complete file lifecycle management: re-uploading creates a FileVersion, downloads are permission-checked and audited (denials audited too), delete goes to trash with restore/purge (including storage cleanup on purge), storage quota is enforced per workspace from usageLimits, and file-level permission overrides work on top of page/block inheritance.

Background

FileVersion (file_versions: fileId, version, key, size, createdBy, unique [fileId, version]) and File.deletedAt (soft delete) exist. Permission model: granteeType (user/team/role), resourceType incl. "file", resourceId, level (NONE/VIEW/COMMENT/EDIT/FULL_ACCESS), inheritsFrom. Workspace.usageLimits Json (TASK-0101 defines the shape). Quota accounting: sum of File.size over non-deleted files per workspace (materialized counter usageStorageBytes on Workspace? — decision: compute via a counter column updated in the file service to avoid O(n) sums; add the column in this task's migration). Audit: AuditLog model exists; required events file.uploaded, file.downloaded, file.download_denied, file.deleted, file.restored, file.purged, file.version_created, file.permission_changed.

Requirements

  1. Versions: re-uploading to an existing file (replaceFileId in the upload session, UI: "Replace file" on a file block/card) stores a new object key and inserts FileVersion { version: n+1 }; the old object is retained (no delete) until purge or an explicit "Delete old versions" action (admin, confirms, deletes orphan keys + rows, keeps the current). File.key/size/metadata/thumbnail always point at the current version; GET /api/v1/files/:id/versions lists them; POST /api/v1/files/:id/rollback (FULL_ACCESS) makes version n current (key swap, no new object).
  2. Download permission + audit: GET /api/v1/files/:id/download + .../preview + GET /api/v1/files/:id (metadata) resolve the effective level: explicit Permission rows for resourceType: "file" (most specific wins; NONE level = explicit deny overrides inheritance) → else inherit from the containing block → page (Phase-3 permission service). Minimum VIEW. On success: audit file.downloaded (actor, workspace, resourceId, ip). On denial: 403 + audit file.download_denied (with the attempted actor + path). VIRUS_DETECTED files: 403 FILE_QUARANTINED + audit (no signed URL ever issued).
  3. Trash: DELETE /api/v1/files/:id (EDIT on the file's page or file grant with ≥ EDIT) sets deletedAt (soft), keeps the object + versions, removes from search index (emit file.deleted event — add to @factory/shared/events), audit file.deleted. Restore (POST /api/v1/files/:id/restore, same permissions) clears deletedAt, re-indexes (re-emit file.uploaded with restored: true flag), audit file.restored. Purge (FULL_ACCESS or workspace Admin/Owner; from the workspace trash view, Phase 3): deletes object + all version objects + thumbnails + DB rows (File, FileVersion), audit file.purged (irreversible — confirm dialog in UI).
  4. Storage quota: effective quota = usageLimits.storageQuota (plan default if unset). Accounting: Workspace.usageStorageBytes (BigInt column, migration) updated transactionally on: complete (+size), purge/replace-old-versions (−size), restore (no change). Upload session pre-check (TASK-0101) uses usageStorageBytes (not a live sum). When over: QUOTA_EXCEEDED naming used/limit. Dashboard: workspace settings shows "Storage: X / Y used" bar (settings UI hook from Phase 2).
  5. File-level permission UI: file block context menu + file browser row → "Permissions…" dialog listing explicit grants (user/team/role × level) with add/remove (same component pattern as page sharing, Phase 3), "Inherit from page" shown as the default state; changes → Permission rows + permission.changed event (re-index) + audit file.permission_changed.
  6. Listing/management endpoints: PATCH /api/v1/files/:id (rename: name only; size/mime immutable), trash list GET /api/v1/files/trash (workspace files, deletedAt set), purge DELETE /api/v1/files/:id?purge=true (guarded).

Acceptance Criteria

Backend Changes

Frontend Changes

Database Changes

API Changes

Tests

Security

Documentation

Dependencies

Definition of Done


TASK-0105

Title

Files E2E + security suite (500 MB boundary, EICAR, MIME spoof, path traversal, unauthorized access)

Objective

Prove the Phase-9 exit criteria and the file security model with one dedicated E2E + security test suite: happy-path previews for every supported kind, the 500 MB / 50 MB plan boundaries, malware blocking, MIME spoofing rejection, filename sanitization, and unauthorized-access denial with audit.

Background

Tasks 0101–0104 ship storage, pipeline, UI, and management. The CI Docker profile includes ClamAV (TASK-0102) and MinIO (Phase 0). tests/e2e/ is Playwright; tests/integration/ is API+Postgres+Redis+OpenSearch+storage. Fixture files live in tests/fixtures/files/. The 500 MB upload must use multipart-friendly streaming (presigned PUT of a sparse file) and must run in CI without exceeding disk limits (sparse file on the runner).

Requirements

  1. E2E happy path (tests/e2e/files.spec.ts): (a) upload an image (PNG) → renders inline + a thumbnail appears in the file picker grid; (b) paste a screenshot (clipboard) → renders; (c) upload a PDF → preview iframe + first-page render visible; (d) upload an MP4 → native player plays; (e) upload a DOCX → download card with icon + size, Download delivers the file.
  2. Size boundaries: (a) free plan (50 MB/file): a 60 MB upload is rejected at session creation with 413 FILE_TOO_LARGE (clear message naming the 50 MB limit) — E2E (toast) + integration (raw API); (b) paid plan (500 MB/file): a 500 MB file (sparse, streaming) uploads via presigned PUT and reaches READY (E2E with a 500 MB fixture generated on the fly; assert status READY + size exact; this is the ROADMAP exit criterion "upload of 500 MB file works (multipart)").
  3. Malware: EICAR test file (standard anti-virus test string) uploaded on the ClamAV CI profile → status VIRUS_DETECTED; the file block renders the blocked card; GET /download → 403 FILE_QUARANTINED; audit row file.download_denied (or file.quarantine_denied) present; the file is absent from search results; no thumbnail keys in the bucket.
  4. MIME spoof: a text file containing plain ASCII, renamed evil.png (simulating an exiftool-style rename without re-encoding) → session accepted (name-based), PUT completes, complete runs magic-byte sniff → 422 MIME_MISMATCH, status FAILED, object deleted from the bucket, block shows the failure state (integration + E2E for the block state).
  5. Path traversal: filenames ../../../etc/cron.d/x, ..\\..\\win.ini, a/../../b.png, bad\x00name.png → stored object keys are always files/{ws}/{fileId}/{v}/<sanitized> (assert the actual S3 key via the storage client in integration: no .., no leading /, no NUL); the displayed name is sanitized but recognizable; no request ever produces a key outside the workspace prefix (property-style test over 200 generated malicious names).
  6. Unauthorized access matrix (integration, 6 cases): non-member GET metadata → 403 + audit; non-member download → 403 + audit; member of a workspace but no page access → 403 on a file inside that page; explicit file VIEW grant to a non-member → 200; grant revoked → 403 (and search re-indexed out); signed URL after 900 s → 403 from storage; signed URL for file A used against file B's key → 403 (key binding, TASK-0101).
  7. Reporting: the suite writes a summary to tests/e2e/reports/files-security.json (scenarios, pass/fail, timing) for CI artifacts; any failure blocks the phase (exit criterion).

Acceptance Criteria

Backend Changes

Frontend Changes

Database Changes

API Changes

Tests

Security

Documentation

Dependencies

Definition of Done

TASK-0105 verification record (rule 30 — evidence, not assertion)

TASK-0105 (files E2E + security suite) — recorded decisions & conflicts (rule 29)