fix: finalize v6 migrations and document workflows

This commit is contained in:
Amruth Pillai
2026-10-01 07:06:15 +02:00
parent 8105889b0c
commit bc92b8cf7f
177 changed files with 78971 additions and 131255 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ rss: true
## Breaking changes & upgrade actions
- **Stop v5 before upgrading the database.** Seven migrations introduce document Trash, richer version history, application outcomes, structured letters and document-bound conversations, then remove `application.archived` and `resume_version.label`. Back up the database and uploads, stop every v5 instance, and deploy v6 together. A mixed v5/v6 rolling upgrade is unsupported. Follow [Upgrading to v6](/self-hosting/upgrading-to-v6), including its rollback procedure. [cbc76b03b](https://github.com/reactive-resume/reactive-resume/commit/cbc76b03b)
- **Stop v5 before upgrading the database.** Ten migrations introduce document Trash, richer version history, application outcomes, structured letters, document-bound conversations and web-access credentials, then remove `application.archived`, `resume_version.label` and the retired Firecrawl credential table. Back up the database and uploads, stop every v5 instance, and deploy v6 together. A mixed v5/v6 rolling upgrade is unsupported. Follow [Upgrading to v6](/self-hosting/upgrading-to-v6), including its rollback procedure. [cbc76b03b](https://github.com/reactive-resume/reactive-resume/commit/cbc76b03b)
- **Cover letters become independent documents.** The migration saves every embedded letter, including hidden letters, as a separate letter linked to its resume's sender details and design, then removes the letter sections from the resume. Open migrated letters in **Documents**. Older imports, stale tabs, API writes and restored versions also move embedded letters into documents without creating duplicates for unchanged content. [250650953](https://github.com/reactive-resume/reactive-resume/commit/250650953)
- **Convert old visual style rules manually.** PDFs now use Semantic CSS exclusively. Stored rules from the old visual style editor need the bundled `apps/server/dist/migrate-legacy-styles.mjs` script; startup does not convert them. Run its dry run first, then `--apply --backup <file>` on persistent storage. Unconverted documents use their template defaults. See [the conversion steps](/self-hosting/upgrading-to-v6#convert-styles-from-the-old-style-editor). Old JSON files imported through the browser are converted during import. [a325232a0](https://github.com/reactive-resume/reactive-resume/commit/a325232a0), [92459122c](https://github.com/reactive-resume/reactive-resume/commit/92459122c)
- **Write structured dates in API clients.** Dated entries use `dates`, with `start`, `end`, `present` and preserved `raw` text. Saves regenerate `period` or `date` from that structure, so editing the display text alone is overwritten. Update clients to write `dates` and set `metadata.page.dateFormat` where needed. Inputs without structured dates still derive them from their text. [bda01febd](https://github.com/reactive-resume/reactive-resume/commit/bda01febd), [cbc76b03b](https://github.com/reactive-resume/reactive-resume/commit/cbc76b03b)
@@ -110,7 +110,7 @@ unreleased changes outside that commit audit.
## Assistant & AI providers
- **Enable shared AI and Firecrawl services for all users.** Self-hosters can set `AI_PROVIDER`, `AI_MODEL`, `AI_API_KEY` and an optional `AI_BASE_URL`, plus `FIRECRAWL_API_URL` and/or `FIRECRAWL_API_KEY`. Each globally configured integration takes precedence over personal credentials and hides their settings controls; saved personal keys remain available after shared configuration is removed. Shared credentials stay on the server and need no `ENCRYPTION_SECRET`; encrypted personal AI and Firecrawl Cloud keys remain supported when it is set. See [Job search and AI](/self-hosting/job-search-and-ai).
- **Enable shared AI and web access for all users.** Self-hosters can set `AI_PROVIDER`, `AI_MODEL`, `AI_API_KEY` and an optional `AI_BASE_URL`, plus `WEB_ACCESS_PROVIDER`, `WEB_ACCESS_API_KEY` and an optional Firecrawl `WEB_ACCESS_API_URL`. Each globally configured integration takes precedence over personal credentials and hides their settings controls; saved personal keys remain available after shared configuration is removed. Shared credentials stay on the server and need no `ENCRYPTION_SECRET`; encrypted personal AI and web-access keys remain supported when it is set. See [Job search and AI](/self-hosting/job-search-and-ai).
<Frame caption="The assistant works beside the document it is helping with">
<img
+1 -1
View File
@@ -5,7 +5,7 @@ description: "Shared search and reading contracts, provider ownership, safety an
Applications and the Assistant share `searchWeb` and `readPage` in
`packages/api/src/features/web-access/`. Keep generic retrieval there; JobPosting parsing and the `job posting`
query suffix belong in Applications. Firecrawl uses its installed SDK; Tavily and Exa use Node fetch with validated
query suffix belong in Applications. All three providers use the shared bounded JSON transport with validated
responses. Select one adapter by the provider discriminant, without a plugin registry or paid-provider fan-out.
## Shared contract
+24 -24
View File
@@ -53,7 +53,7 @@ No separate search, scraping, extraction, and research setup is required. AI con
3. Preserve the source link and saved description used for preparation; show retrieval information for fetched content. Refreshing a posting must not silently replace the evidence behind existing documents.
4. Connect saving to existing resume selection, copy-for-job, manual editing, and assistant review flows. Keep letters optional.
5. Make external application handoff and confirmed submission distinct, using existing sent-document history.
6. Support Firecrawl, Tavily, and Exa for both search and page reading through shared application-owned functions. Preserve existing Firecrawl credentials, custom server endpoints, URL protections, and built-in fallback.
6. Support Firecrawl, Tavily, and Exa for both search and page reading through shared application-owned functions. Preserve custom server endpoints, URL protections, and built-in fallback.
7. Connect those same functions to the existing AI SDK assistant and support verified native-search configurations for OpenAI, Anthropic, and Gemini. Complete capability selection, source display, cancellation, and error handling in the same release.
8. Replace Firecrawl-specific settings and feature gates with one optional web connection and capability-based availability. Retain optional search without promoting broad job discovery as a new product promise.
@@ -63,15 +63,15 @@ These changes should reuse existing application records and pipeline stages. Pre
### Provider choice
| External provider | Search | Read a supplied URL | Reason to include |
| ----------------- | ------------------- | ------------------- | --------------------------------------------------------------------------------- |
| Firecrawl | Existing Search API | Existing Scrape API | Preserve current users, custom endpoints, and self-hosted deployments. |
| Tavily | Search API | Extract API | One alternative connection covers both operations. |
| Exa | Search API | Contents API | A third independent backend covers both operations without another setup concept. |
| External provider | Search | Read a supplied URL | Reason to include |
| ----------------- | ---------- | ------------------- | --------------------------------------------------------------------------------- |
| Firecrawl | Search API | Scrape API | Support custom endpoints and self-hosted deployments. |
| Tavily | Search API | Extract API | One alternative connection covers both operations. |
| Exa | Search API | Contents API | A third independent backend covers both operations without another setup concept. |
These are three actual external search providers; native LLM search is additional support and does not substitute for the three-provider requirement. Each can be used independently. Supporting all three does not mean configuring or calling all three.
Keep the installed Firecrawl SDK. Implement the two small Tavily and Exa HTTP adapters with Node's built-in fetch and Zod response validation. The application needs their search/read endpoints, not their entire SDKs. Use the installed AI SDK's `tool()` to expose shared functions to the assistant; adding provider-specific AI SDK tool packages would duplicate the configuration and normalization needed by ordinary UI requests.
Implement all three HTTP adapters with the existing bounded JSON transport and Zod response validation. The application needs their search/read endpoints, not their entire SDKs. Use the installed AI SDK's `tool()` to expose shared functions to the assistant; adding provider-specific AI SDK tool packages would duplicate the configuration and normalization needed by ordinary UI requests.
For Tavily imports, request Markdown extraction without query-based chunk reranking and inspect per-URL failures even on HTTP 200. For Exa imports, request page text rather than highlights or summaries and use the documented freshness controls; the older `livecrawl` option is deprecated. Search requests should avoid full-page extraction and generated answers. Fetch a selected result only when it is needed. [Tavily Search](https://docs.tavily.com/documentation/api-reference/endpoint/search), [Tavily Extract](https://docs.tavily.com/documentation/api-reference/endpoint/extract), [Exa Search](https://exa.ai/docs/reference/search), [Exa Contents](https://exa.ai/docs/reference/get-contents).
@@ -132,19 +132,19 @@ Native-search support is limited to verified model/endpoint combinations and doc
Store one selected external connection per user: provider plus encrypted API key. Reuse existing credential encryption. Do not create a table or settings card per provider, store several inactive keys, or require separate defaults for search and reading.
Add a generic `web_access_credentials` table and backfill existing Firecrawl ciphertext as provider `firecrawl` in a generated migration, without decrypting it or asking users to enter keys again. The new service becomes the sole runtime source. Retain the old table only as a compatibility/rollback artifact, not another active configuration source; replacement or deletion of a user's connection must also remove any obsolete legacy key for that user. Review SQL and verify backup/restore before deployment. Application rollback after configuration changes must account for the new credentials rather than assuming old binaries understand them.
Use the generic `web_access_credentials` table as the sole credential source. The pre-release Firecrawl API has no users or compatibility promise; remove its handlers, environment aliases and retired credential table. Keep historical migrations and generate a migration that drops the obsolete table. Review SQL and verify backup/restore before deployment.
Expose generic status, save, delete, and test procedures under `/integrations/web-access`. Keep the existing Firecrawl endpoints as narrow compatibility handlers. Legacy reads report Firecrawl availability only when Firecrawl is selected. Legacy writes must not overwrite or delete an active Tavily/Exa connection; return a conflict directing clients to the generic endpoint. Keep all existing server-managed and encryption-precondition checks.
Expose status, save, delete, and test procedures under `/integrations/web-access`. Keep all server-managed and encryption-precondition checks.
Server configuration uses `WEB_ACCESS_PROVIDER`, `WEB_ACCESS_API_KEY`, and an optional `WEB_ACCESS_API_URL` for a custom Firecrawl service. Tavily and Exa use their official fixed endpoints. Preserve `FIRECRAWL_API_KEY` and `FIRECRAWL_API_URL` as aliases when no generic configuration is supplied. Explicit generic configuration wins; partial or inconsistent generic configuration fails validation rather than silently falling back. A self-hosted Firecrawl endpoint may remain keyless as today. Personal connections use cloud endpoints and do not expose arbitrary base URLs.
Server configuration uses `WEB_ACCESS_PROVIDER`, `WEB_ACCESS_API_KEY`, and an optional `WEB_ACCESS_API_URL` for a custom Firecrawl service. Tavily and Exa use their official fixed endpoints. Partial or inconsistent configuration fails validation. A self-hosted Firecrawl endpoint may be keyless. Personal connections use cloud endpoints and do not expose arbitrary base URLs.
Resolver order is explicit server configuration, legacy server Firecrawl configuration, personal connection, then built-in reading only. Server-managed configuration continues to disable personal credential changes. Resolve credentials for each request/run; never keep a singleton client containing a user's key.
Resolver order is server configuration, personal connection, then built-in reading only. Server-managed configuration disables personal credential changes. Resolve credentials for each request/run; never keep a singleton client containing a user's key.
Status should communicate available capabilities, selected provider, managed/personal ownership, and whether keys can be changed, without returning secrets. A user-triggered connection test probes search and reading independently and reports actual results, including unsupported self-hosted search or quota failures. The test must bypass reader fallback so a successful built-in fetch cannot falsely validate a broken provider. Do not retest or incur external calls every time settings renders.
### UI and operational behavior
Replace the Firecrawl settings section with a single provider selector inside the optional connection form: Firecrawl, Tavily, Exa. Use generic availability to gate Applications search. Existing Firecrawl users see their connection already selected after migration. New users see a working built-in reader and an optional connection action.
Use a single provider selector inside the optional connection form: Firecrawl, Tavily, Exa. Use generic availability to gate Applications search. New users see a working built-in reader and an optional connection action.
Complete the Save/Applied, source review, truncation recovery, preparation, and submission changes from the product scope in the same release. Every provider must pass through the same workflow and receive the same error treatment.
@@ -156,16 +156,16 @@ Log provider, operation, duration, safe failure category, and fallback outcome.
Work in the following dependency order and release only when the complete end state passes. These are implementation tasks, not separate product increments.
| Order | Work | Main existing owners |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Fix the shared contracts, selection rules, and final provider set before editing consumers. | `packages/api/src/features/web-access/`, `packages/ai/src/tools/agent-tool-contracts.ts` |
| 2 | Build all three adapters and move the safe built-in reader; keep job normalization in Applications. | `packages/api/src/features/applications/posting.ts`, new web-access feature |
| 3 | Add one credential migration, resolver, generic integration router, and legacy compatibility handlers. | `packages/db/src/schema/firecrawl.ts`, `migrations/`, `packages/api/src/features/firecrawl/`, `packages/api/src/routers/index.ts` |
| 4 | Wire Applications and the assistant to the shared service, including native search, tool contracts, source rendering, cancellation, and errors. | `applications/ai.ts`, `agent/tools.ts`, `agent/service.ts`, `ai/capabilities.ts`, `ai/service.ts`, web assistant feature |
| 5 | Finish the one-connection settings UI and agreed job-saving/preparation workflow against the final contracts. | Web settings, Applications, document-copy/detail features, application schema/DTOs |
| 6 | Update environment validation, docs, API spec, translations, and deployment checks; run the whole acceptance matrix. | `packages/env/src/server.ts`, `.env.example`, `turbo.json`, self-hosting/AI guides, `docs/spec.json`, Lingui catalogs |
| Order | Work | Main existing owners |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Fix the shared contracts, selection rules, and final provider set before editing consumers. | `packages/api/src/features/web-access/`, `packages/ai/src/tools/agent-tool-contracts.ts` |
| 2 | Build all three adapters and move the safe built-in reader; keep job normalization in Applications. | `packages/api/src/features/applications/posting.ts`, new web-access feature |
| 3 | Use one credential table, resolver and generic integration router; retire pre-release compatibility code. | `packages/db/src/schema/web-access.ts`, `migrations/`, `packages/api/src/features/web-access/`, `packages/api/src/routers/index.ts` |
| 4 | Wire Applications and the assistant to the shared service, including native search, tool contracts, source rendering, cancellation, and errors. | `applications/ai.ts`, `agent/tools.ts`, `agent/service.ts`, `ai/capabilities.ts`, `ai/service.ts`, web assistant feature |
| 5 | Finish the one-connection settings UI and agreed job-saving/preparation workflow against the final contracts. | Web settings, Applications, document-copy/detail features, application schema/DTOs |
| 6 | Update environment validation, docs, API spec, translations, and deployment checks; run the whole acceptance matrix. | `packages/env/src/server.ts`, `.env.example`, `turbo.json`, self-hosting/AI guides, `docs/spec.json`, Lingui catalogs |
This order avoids rewriting the UI or assistant once per provider. Keep all runtime-specific code in its current owning packages. Check server bundling and package export boundaries; retaining the Firecrawl SDK and using built-in fetch avoids adding two more vendor SDK bundles.
This order avoids rewriting the UI or assistant once per provider. Keep all runtime-specific code in its current owning packages. Check server bundling and package export boundaries; the bounded native transport needs no vendor SDK bundles.
## Acceptance conditions
@@ -179,7 +179,7 @@ This order avoids rewriting the UI or assistant once per provider. Keep all runt
- Existing Firecrawl search, rendered reading, custom server URL support, and direct-reader fallback keep working.
- Firecrawl, Tavily, and Exa each work independently for Applications search/import and assistant search/read. Users need only the selected provider's credentials.
- Native assistant search works for verified OpenAI, Anthropic, and Gemini configurations without an external web connection. Unsupported combinations fail clearly or use an explicitly configured external connection.
- Explicit provider selection is respected, existing credentials migrate, and legacy API calls cannot overwrite another provider's configuration.
- Explicit provider selection is respected; each account has one encrypted connection, with changes disabled under server configuration.
- All web tools show correct progress, errors, and validated sources after streaming and after reloading a conversation. Cancellation stops outstanding retrieval.
- Core behavior works on Docker and Vercel without adding required services or containers.
@@ -191,7 +191,7 @@ Use the existing Vitest and Playwright setup. Add focused behavior checks; no ne
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Adapter contract | Table-driven mocked HTTP checks for all three providers: result mapping, per-URL errors, auth/quota failures, malformed/empty results, timeout, response limit, and abort. |
| URL safety and fallback | Existing private-host/DNS/redirect checks remain effective; failure falls back within budget, while unsafe URLs and abort do not. |
| Credentials and migration | Existing Firecrawl rows survive; users cannot access each other's keys; server precedence, partial env errors, deletion, and legacy-route conflicts behave as specified. |
| Credentials and migration | The retired table is removed; users cannot access each other's keys; server precedence, partial env errors, connection replacement and deletion behave as specified. |
| AI SDK and history | Tool selection, compatible native/custom combinations, capability-aware instructions, sources, stop behavior, and old conversation rendering/replay. |
| Product journey | Save/paste/manual preparation without services; all three provider choices through the same UI; failed enrichment retains the posting; Saved stays distinct from Applied; submitted document versions remain correct. |
| Deployment | Affected typechecks/tests, non-mutating lint, package boundaries, production build, and existing serverless artifact checks. Verify Docker and Vercel environment handling. |
@@ -215,7 +215,7 @@ Keep public-URL validation, redirect and network protections, credential encrypt
- Email/calendar synchronization and automatic submission.
- A general plugin platform or custom connector protocol.
Document the adapter contract and contribution checks as part of this release. Evaluate completeness, reliability, latency, deployment behavior, and cost per usable import; published feature lists alone do not establish a best hosted default. Existing installs keep Firecrawl, while new installs start with the built-in reader and no commercial connection.
Document the adapter contract and contribution checks as part of this release. Evaluate completeness, reliability, latency, deployment behavior, and cost per usable import; published feature lists alone do not establish a best hosted default. New installs start with the built-in reader and no commercial connection.
## Implementation verification
+1 -3
View File
@@ -189,8 +189,6 @@ for Tavily/Exa, or a custom URL for those providers stops startup instead of sil
| `WEB_ACCESS_PROVIDER` | unset | One selected provider: `firecrawl`, `tavily` or `exa`. |
| `WEB_ACCESS_API_KEY` | unset | Shared provider key. Required except for Firecrawl with an explicit custom URL. |
| `WEB_ACCESS_API_URL` | Firecrawl Cloud | Optional Firecrawl base URL without `/v2`, e.g. `http://localhost:3102`. Private service URLs are operator-controlled. Tavily and Exa always use their official endpoints. |
| `FIRECRAWL_API_URL` | unset | Legacy Firecrawl URL alias, used only when no `WEB_ACCESS_*` configuration is supplied. A URL alone permits keyless self-hosted Firecrawl. |
| `FIRECRAWL_API_KEY` | unset | Legacy Firecrawl key alias. A key alone selects Firecrawl Cloud when no generic configuration is supplied. |
Connections enable Applications keyword search and assistant web tools. URL import uses the selected reader, then
falls back to the built-in reader on recoverable failures. Saving pasted text and manual preparation work without
@@ -201,7 +199,7 @@ until the user reviews and saves it.
does not call external services. Quota and authentication failures leave manual workflows available.
See [Job search and AI](/self-hosting/job-search-and-ai) for connection setup, Firecrawl deployment, optional SearXNG
search and migration/rollback guidance. Public page URLs remain protected against private destinations, redirects
search and backup guidance. Public page URLs remain protected against private destinations, redirects
and DNS rebinding. A remote reader must enforce these protections internally too. Search queries and selected URLs
go to the chosen service; resume data and AI keys do not.
+8 -15
View File
@@ -43,8 +43,8 @@ You can mix these choices, such as shared web access with personal AI keys. See
## 2. Connect Firecrawl
Follow the official [Firecrawl self-hosting guide](https://docs.firecrawl.dev/contributing/self-host) to deploy its
API and supporting services. Use the release and Compose files recommended there; the npm SDK included in Reactive
Resume is a client and does not run the service.
API and supporting services. Use the release and Compose files recommended there. Reactive Resume connects to
that service over HTTP.
Add the connection to **Reactive Resume's** environment:
@@ -64,8 +64,7 @@ in Reactive Resume must point to public HTTPS destinations.
For Firecrawl Cloud, use its [quickstart](https://docs.firecrawl.dev/introduction) to get a key, set only
`WEB_ACCESS_PROVIDER="firecrawl"` and `WEB_ACCESS_API_KEY`, and leave `WEB_ACCESS_API_URL` unset. Requests then go to
`https://api.firecrawl.dev`. Tavily and Exa use fixed official endpoints and require a key. Supplying their own API URL
is rejected. Existing `FIRECRAWL_API_KEY`/`FIRECRAWL_API_URL` variables remain aliases when every generic variable is
unset; partial generic configuration never falls back to those aliases.
is rejected. Incomplete web-access configuration fails startup.
### Container networking and local development
@@ -171,18 +170,12 @@ and restart or redeploy it. Keep keys in server configuration or secret storage,
Reading failures can fall back to the built-in reader, so a successful URL import alone does not prove the external
service was used. Review the displayed posting source and use the independent connection test.
## Existing credentials and rollback
## Credential backups
The migration copies Firecrawl ciphertext unchanged into `web_access_credentials` with provider `firecrawl`; users
keep their selected connection without entering keys again. The generic table is the sole runtime source. The old
`firecrawl_credentials` table remains as a rollback artifact, and replacing or removing a connection removes that
user's obsolete legacy key. Legacy API endpoints cannot overwrite or delete a selected Tavily/Exa connection.
Back up PostgreSQL and preserve `ENCRYPTION_SECRET` before deployment. Restore the backup into a disposable database
and verify existing keys resolve before upgrading the live installation. If rolling back after connection changes,
restore the matching pre-upgrade backup or explicitly migrate current Firecrawl credentials back to the legacy table;
old binaries cannot read Tavily/Exa credentials. Retaining the old table alone does not restore keys removed during
connection changes.
Personal connections store encrypted keys in `web_access_credentials`. Preserve `ENCRYPTION_SECRET` with the
PostgreSQL backup so restored keys can be decrypted. Verify restored connections in a disposable installation
before upgrading the live installation. The retired Firecrawl API, environment aliases and credential table are
removed; all providers use `/integrations/web-access` and the `WEB_ACCESS_*` variables.
## Opt-in live connection check
+24 -22
View File
@@ -9,11 +9,11 @@ This page is for installations already on v5. Coming from v4? Follow [Migrating
## What to expect
- **Migrations run on startup**, as in v5. Seven new migrations add columns and tables, move data, and drop two legacy columns. You don't run anything by hand.
- **v5 can't run against the upgraded database.** The last migrations remove columns that v5 reads, so stop every v5 instance before the first v6 instance starts. There's no mixed v5 and v6 rolling update.
- **Migrations run on startup**, as in v5. One v6 migration adds columns and tables, moves data, and drops two legacy columns. You don't run anything by hand.
- **v5 can't run against the upgraded database.** The v6 migration removes columns that v5 reads, so stop every v5 instance before the first v6 instance starts. There's no mixed v5 and v6 rolling update.
- **Cover letters leave resumes.** Every letter stored inside a resume becomes a saved letter of its own.
- **Old style-editor styles need a one-time manual conversion**, with a script you run yourself. Nothing converts them automatically.
- **No new environment variables.** Everything you set for v5.3 keeps working. `REDIS_URL` is now optional for the assistant.
- **New optional environment variables** configure server AI and web access. Existing v5.3 settings keep working. Reverse proxies need `TRUSTED_PROXIES` to preserve per-visitor authentication limits; `REDIS_URL` is now optional for the assistant outside Vercel.
- **Vercel projects must switch to the Services framework preset** before they deploy v6.
## Before you upgrade
@@ -131,21 +131,21 @@ PostgreSQL 18 changes the default data layout. Changing the image tag alone does
## What the migrations change
Every migration runs once, in order, under an advisory lock. Three of them ship a `rollback.sql` next to `migration.sql` in the repository's `migrations/` folder.
Existing v5 migrations stay unchanged. The unreleased v6 changes are consolidated in `20261001042749_v6_release`, generated from the current schema with the v5 data conversions included once. It runs under an advisory lock and ships one `rollback.sql` alongside `migration.sql`.
| Migration | What it does | Rollback script |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `20260928171330_resume_version_kinds` | Adds a `kind`, `name`, and session to resume versions (filled in from the old labels) and a `resume_slug_redirect` table, so a renamed public link keeps working for 30 days. | No (additive) |
| `20260928175116_documents_trash_and_links` | Adds Trash (`trashed_at`) to resumes and letters, tags and locking to letters, and a link from a resume to the application it was made for. | No (additive) |
| `20260928193941_applications_closed_and_sent` | Adds the **Closed** stage with a reason, and what was sent with an application. Turns `rejected` applications into closed (not selected) and archived ones into closed. Gives an application its letter when exactly one letter was written for it. | Yes |
| `20260928201742_letters_structured_and_versions` | Adds structured recipient fields, links to a resume's details and design, and version history (`cover_letter_version`) for letters. Existing letters keep their free-form layout and their own copied details. | No (additive) |
| `20260928211634_assistant_documents_and_outcomes` | Lets assistant conversations belong to a letter and count proposed and accepted edits. | No (additive) |
| `20260929063245_contract_redesign_legacy_fields` | Closes anything still archived or rejected, then drops `application.archived` and `resume_version.label`. After this, v5 no longer works against the database. | Yes |
| `20260929071322_letters_leave_resumes` | Saves every cover letter stored inside a resume as a letter of its own, then removes the cover-letter sections from resumes and their page layouts. | Yes |
| Area | What changes |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Resume versions | Adds `kind`, `name`, and session fields, fills kinds from old labels, then removes `label`. |
| Public links | Adds `resume_slug_redirect`, so renamed public links keep working for 30 days. |
| Documents | Adds Trash to resumes and letters, tags and locking to letters, and links from resumes to applications. |
| Applications | Adds the Closed stage and reason, sent document versions and Check score, requirements, and posting provenance. Converts rejected and archived applications once, updates their timeline, links unambiguous letters, then removes `archived`. |
| Letters | Adds structured recipient fields, live links to resume details and design, and version history. Saves embedded resume letters as separate documents and removes their sections from resumes and page layouts. |
| Assistant | Adds conversations about letters and counts of proposed and accepted edits. |
| Web access | Creates `web_access_credentials` directly for encrypted per-user connections. |
### Cover letters leaving resumes
In v5, a resume could hold cover letters as a section. The last migration moves each of them, hidden ones included, into a saved letter:
In v5, a resume could hold cover letters as a section. The v6 migration moves each of them, hidden ones included, into a saved letter:
- The letter is named after the resume and the section, for example "Product Designer — Cover letter".
- It's linked to the resume, and takes its sender details and design from it, so it looks as it did inside the resume.
@@ -224,8 +224,12 @@ See [Using the MCP server](/guides/using-the-mcp-server) and [Managing applicati
## Environment variables
v6 adds no environment variables and removes none. Two behaviors changed:
Existing variables remain supported. New settings are optional unless your deployment uses the corresponding feature:
- `AI_PROVIDER`, `AI_MODEL`, `AI_API_KEY`, and `AI_BASE_URL` configure a server AI provider. Set provider, model, and key together; Ollama may omit the key. `openai-compatible` also requires a base URL. Configuring this shared provider disables personal AI providers. Leave these unset to continue using user-configured providers.
- `WEB_ACCESS_PROVIDER`, `WEB_ACCESS_API_KEY`, and `WEB_ACCESS_API_URL` configure server web access. Providers are Firecrawl, Tavily, and Exa. Set provider and key together; only Firecrawl accepts a custom API URL, which may be keyless.
- `TRUSTED_PROXIES` lists comma-separated proxy IP addresses or CIDRs. Behind nginx, Caddy, or Traefik, include the immediate proxy and any trusted intermediate hops. The proxy must append or replace `X-Forwarded-For` with the actual client address. Leave this unset for direct connections. Never include public client networks: forwarded headers are trusted only through configured proxy addresses.
- `FLAG_DISABLE_API_RATE_LIMIT` now disables all API and authentication limits, including PDF exports and AI requests. Keep it `false` on public installations.
- `REDIS_URL` is optional for the assistant. Without Redis, replies can't resume after a page reload, and **Stop** reaches only a reply running on the same server. `ENCRYPTION_SECRET` is still required for AI providers and the assistant. Vercel still requires Redis.
- The web build now also produces `apps/web/dist-prerender` (localized marketing pages). The Docker image includes it. If you build and deploy the server yourself, copy it next to `apps/web/dist`; without it, those pages render in the browser instead.
@@ -244,23 +248,21 @@ Restoring the backup you took before the upgrade is the safest way back, and the
If you must keep data written since the upgrade, you can undo the migrations that v5 can't live with, by hand, before starting v5:
1. Stop every v6 instance.
2. Run the rollback scripts with `psql`, newest first:
2. Run the consolidated rollback script with `psql`:
```bash
psql "$DATABASE_URL" -f migrations/20260929071322_letters_leave_resumes/rollback.sql
psql "$DATABASE_URL" -f migrations/20260929063245_contract_redesign_legacy_fields/rollback.sql
psql "$DATABASE_URL" -f migrations/20260928193941_applications_closed_and_sent/rollback.sql
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f migrations/20261001042749_v6_release/rollback.sql
```
The first puts letters back into the resumes they came from (one section per resume per run; run it again for a resume that had several). The second restores `application.archived` and `resume_version.label`. The third turns closed applications back into `rejected` or archived.
The script puts letters back into the resumes they came from (one section per resume per run; run it again for a resume that had several), restores `application.archived` and `resume_version.label`, and turns closed applications back into `rejected` or archived. These steps run in one transaction.
3. Start v5.
Columns and tables added by v6 stay; v5 ignores them. The letters saved from resumes stay too, so in v5 each one shows both inside its resume and in the cover letter library. Documents in Trash show up again in v5, because v5 doesn't know about Trash. If you converted old styles, the converted stylesheets stay, and `--restore` with your backup file puts the originals back.
<Warning>
The rollback scripts don't change the migration ledger, so the v6 migrations stay recorded as applied. Upgrading the
same database to v6 again won't re-run them. Take a fresh backup and ask in [GitHub
The rollback script doesn't change the migration ledger, so the v6 migration stays recorded as applied. Upgrading the
same database to v6 again won't re-run it. Take a fresh backup and ask in [GitHub
Discussions](https://github.com/reactive-resume/reactive-resume/discussions) before you try.
</Warning>
-469
View File
@@ -20265,475 +20265,6 @@
}
}
},
"/integrations/firecrawl": {
"get": {
"operationId": "getFirecrawlStatus",
"summary": "Get Firecrawl availability",
"tags": [
"Integrations"
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"managed": {
"type": "boolean"
},
"configured": {
"type": "boolean"
},
"canSave": {
"type": "boolean"
}
},
"required": [
"managed",
"configured",
"canSave"
]
}
}
}
}
}
},
"put": {
"operationId": "saveFirecrawlKey",
"summary": "Save a personal Firecrawl Cloud key",
"tags": [
"Integrations"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"minLength": 1,
"maxLength": 2000
}
},
"required": [
"apiKey"
]
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"not": {}
},
{
"not": {}
}
]
}
}
}
},
"403": {
"description": "403",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "FORBIDDEN"
},
"status": {
"const": 403
},
"message": {
"type": "string",
"default": "Firecrawl is managed by the server."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"409": {
"description": "409",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "CONFLICT"
},
"status": {
"const": 409
},
"message": {
"type": "string",
"default": "Manage the selected provider through /integrations/web-access."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "Credential encryption is not configured."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
}
},
"delete": {
"operationId": "deleteFirecrawlKey",
"summary": "Delete a personal Firecrawl Cloud key",
"tags": [
"Integrations"
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"not": {}
},
{
"not": {}
}
]
}
}
}
},
"403": {
"description": "403",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "FORBIDDEN"
},
"status": {
"const": 403
},
"message": {
"type": "string",
"default": "Firecrawl is managed by the server."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"409": {
"description": "409",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "CONFLICT"
},
"status": {
"const": 409
},
"message": {
"type": "string",
"default": "Manage the selected provider through /integrations/web-access."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "Credential encryption is not configured."
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
}
}
},
"/resumes/tags": {
"get": {
"operationId": "listResumeTags",