diff --git a/docs/applying-custom-styles.mdx b/docs/applying-custom-styles.mdx index 2a6e7444f..54b3ee4b9 100644 --- a/docs/applying-custom-styles.mdx +++ b/docs/applying-custom-styles.mdx @@ -1,149 +1,236 @@ --- -title: "Applying Custom Styles" -description: "Use Reactive Resume Semantic CSS to make safe, targeted, and portable changes to your resume PDF." +title: "Applying custom styles" +description: "Write CSS rules that change how your resume PDF looks: pick an element on the page, style it, and learn what the PDF engine supports." --- -Custom Styles let you make focused changes that are not available in the regular **Design**, **Typography**, **Layout**, -**Page**, and **Picture** settings. They use Semantic CSS, a CSS-like language designed for -resume PDFs. +Custom styles let you change details the Design controls don't cover, such as the color of one entry, the spacing +between skills, or a border under every section heading. You write them in a CSS-like language called Semantic CSS, +and they apply to the PDF: the page preview, PDF downloads, and your public resume. DOCX and Markdown exports +don't use them. + +Try the regular **Design** controls first (template, type, color, page and layout). They cover most changes and keep +working when you switch templates. Reach for custom styles when you want something specific. + +## Before you start + +- Custom styles are per resume. Each resume has its own stylesheet. +- Nothing checks your CSS as you type. A rule the PDF can't use is skipped without a warning, and the rest still + applies. If you see no change on the page, the rule didn't apply. See [When a rule has no effect](#when-a-rule-has-no-effect). +- Custom styles can't add content, load fonts or images, or change what a section contains. They only restyle what's + already on the page. + +## Open the stylesheet editor + + + + Open your resume and select **Design** at the top of the editor, or press 2 when you're not typing in a + field. + + + Select **Advanced** in the row of groups at the top of the Design panel. The Advanced group opens and scrolls into + view. + + + Scroll to **Custom Styles**, near the end of the Advanced group, just above **Reset to template defaults**. + + + + + The Custom Styles heading with a toolbar of five icons (undo, redo, copy, format, focus mode), two hint lines, and an empty code editor + + +On a phone, Design opens as a sheet over the page. Select the **Page** tab, then select **Advanced** under the page +settings. - Semantic CSS styles the PDF output, not the browser interface. It cannot load fonts, images, scripts, or other resources, and - it cannot create new resume content. + **Reset to template defaults** doesn't touch your custom styles. It resets the look (fonts, colors, spacing) and + keeps your stylesheet, paper size, language, date format and section layout. -## Convert existing Custom Styles +## Style one section or entry -If a resume still uses the previous Custom Styles form, Reactive Resume creates a converted stylesheet draft. Your -current rules remain active while you review it. +The quickest way to start is to pick the thing you want to change on the page. - - Open the resume in the builder, select **Design**, then select **Custom Styles**. + + With the Custom Styles editor open, click the resume header, an entry (for example a job under Experience), or a + section without entries (such as Summary) on the page. - - Check the preview against the legacy result. + + The editor adds a rule for it at the end of your stylesheet, with a comment naming what you picked, and puts the + cursor inside the rule. Type the declarations you want, for example `color: #0f766e;`. - - Select **Activate Semantic CSS** only after the preview matches the legacy result. Reactive Resume never applies both - systems at once, and keeps the original legacy rules available for rollback. + + The page updates as you type. While your cursor is inside a rule, everything the rule styles is outlined with a + dashed line on the page. -## Make your first change + + The stylesheet editor with a rule for the Education entry University of Washington, and the same entry on the page shown in teal inside a dashed outline + -Open the resume you want to style, select **Design**, then select **Custom Styles**. Start with a complete stylesheet: +A picked rule looks like this: ```css -section[type="experience"] > section-heading { +/* Education › University of Washington */ +section[id="education"] item[id="019bef5a-93e4-7746-ad39-48455f6cef9e"] { color: #0f766e; - text-transform: uppercase; } ``` - - - Add the stylesheet to the editor. Start with one visual change so it is easy to review in the preview. - - - The preview updates as you type. If nothing changes, the rule didn't apply; check the selector and property. - - - Add one related change at a time. Your changes use the normal resume autosave and undo history. - - +If the stylesheet already has a rule with that exact selector, clicking the element moves your cursor into the existing +rule instead of adding another one. -## Style one element +Rules made this way use the entry's ID, so they only affect that one entry in this resume. To style every entry of a +kind, use the section type instead (see [Target the right part of your resume](#target-the-right-part-of-your-resume)). -The quickest way to style a single section or entry is to pick it on the page: +## Find an entry by name -1. Open **Design → Advanced → Custom Styles**. -2. Click the section or entry on the page. The editor adds a rule for it, named in a comment, and puts the cursor - inside: +You can also add an entry's selector without clicking the page. Where a selector goes (outside any `{ }`), start +typing part of the entry's title, such as `Senior`. The suggestions include matching sections and entries, shown as +"Section › Entry". Choose one to insert its selector. - ```css - /* Experience › Senior Game Developer */ - section[id="experience"] item[id="019bef5a-93e4-7746-ad39-3a132360f823"] { - color: #0f766e; - } - ``` + + Autocomplete suggestions under the word Senior, led by Experience › Senior Game Developer with its section and item selector + -3. Type the declarations you want. Clicking an element that already has a rule moves the cursor into it. +Autocomplete also suggests element names and attributes in selectors, property names inside a rule, and values after +a colon, including your own variables and the read-only `--resume-*` variables. Press Ctrl +Space to open the suggestions without typing. -While the cursor is inside a rule, everything that rule styles is outlined on the page. You can also type part of an -entry's name (for example `Senior`) in a selector and pick it from the suggestions. +A few more editor helpers: -To style an entry by position instead, use `:nth-of-type`, which counts entries only: +- **Hover** an element name, property or `--resume-*` variable to see a short description. +- **Color swatches** appear after color values. Click one to pick a color from presets or a custom picker; the value + in your CSS changes with it. +- **Search** with ⌘ F (Ctrl F on Windows and Linux). -```css -/* The first experience entry */ -section[type="experience"] item:nth-of-type(1) { - margin-bottom: 12pt; -} -``` +## Use the editor toolbar -## Target the right part of your resume - -Semantic CSS selectors describe resume content rather than a template's internal HTML. Selector and attribute names are -lowercase and case-sensitive. Prefer semantic selectors when you want a style to work across resumes and templates. - -### Start with the resume structure - -| Selector | Targets | Typical use | -| --- | --- | --- | -| `resume` | The complete resume | Scope a rule to one template. | -| `page` | A rendered PDF page | Set a page size. | -| `region` | Header, main, sidebar, or featured region | Style a layout area. | -| `header` | The resume header | Style the identity and contact area. | -| `section` | A resume section | Target a section type or placement. | -| `section-heading` | A section title | Change heading typography or decoration. | -| `section-items` | The items in a section | Adjust item layout and gaps. | -| `item` | One resume item | Control spacing or pagination for an experience, project, or similar item. | -| `item-header` | An item's summary row | Align the title, company, dates, or similar details. | - -### Target header and item content - -| Selector | Targets | Typical use | -| --- | --- | --- | -| `picture` | The profile picture | Change dimensions, crop, border, or picture shadow. | -| `name`, `headline` | Header name and headline | Change the main identity typography. | -| `contact-list`, `contact-item` | Header contact details | Space or restyle contact details. | -| `combined-text` | A template-combined value | Style an item value that combines fields. | -| `field` | A named content field | Target a position, company, date, or other field. | -| `link` | A structured link | Change linked text or layout. | -| `icon`, `level` | An icon or level indicator | Restyle decorative elements. | - -### Target rich text and lists - -| Selector | Targets | +| Button | What it does | | --- | --- | -| `rich-text`, `rich-heading`, `blockquote`, `paragraph` | Rich-text blocks in descriptions and summaries. | -| `list`, `list-item`, `list-marker`, `list-item-content` | Lists, the outer item row, its bullet or number, and its content. | -| `strong`, `emphasis`, `underline`, `strike`, `code`, `text-span`, `mark` | Inline rich-text formatting. | -| `hard-break`, `horizontal-rule` | A forced line break or horizontal rule. | -| `template-part` | A template-provided extension point. Use only with a template guard. | +| **Undo stylesheet edit** / **Redo stylesheet edit** | Steps back or forward through your resume's changes. | +| **Copy stylesheet** | Copies the whole stylesheet to your clipboard. | +| **Format stylesheet** | Tidies indentation and spacing. | +| **Open focus mode** | Opens the editor in a larger side panel titled **Semantic CSS stylesheet**. Select **Exit focus mode** or close the panel to go back. | + +Undo and redo in the stylesheet editor, including ⌘ Z and ⌘ Shift +Z while you type in it, use your resume's shared undo history. If your last change was somewhere else, such +as a template switch, undo reverses that first. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). + +Your stylesheet saves automatically with the rest of the resume, and it's part of every saved version in **History**. + +## When a rule has no effect + +There's no error list or validity checker. The PDF engine applies what it understands and skips the rest: an +unsupported declaration is skipped on its own, and a rule with an invalid selector is skipped as a whole. If a change +doesn't show: + +1. **Check the selector matches something.** Put your cursor inside the rule. If nothing is outlined on the page, the + selector matches nothing. Check spelling (names are lowercase and case-sensitive), attribute values, and whether + the current template has that part. +2. **Check the property is supported** for that element. See the [properties](#properties) and + [engine limits](#engine-limits) below. For example, `background-color` works on containers such as `section` and + `header`, but not on text elements such as `field`. +3. **Simplify.** Reduce the rule to one selector and one declaration, confirm it works, then add more. +4. **Check the size.** A stylesheet larger than 128 KB, or with more than 1,024 rules, is ignored completely. + + + Check the downloaded PDF before you send or share a resume with custom styles. Page breaks and template-specific + details can make a rule look different from what you intended. + + +### Engine limits + +The PDF engine accepts these without an error but doesn't draw them: + +- Rotation (`transform: rotate(...)`). The picture's rotation setting has no effect for the same reason. +- Dashed and dotted borders. They draw as solid lines. +- Percentages for padding, margin, gaps and font sizes. Percentages work only for `width`, `height`, their `min-` and + `max-` forms, `flex-basis`, `left` and `right`. +- `z-index`, `max-lines`, `text-indent`, `vertical-align`, `object-position` and `-resume-min-presence-ahead`. + +Two text limits apply whether or not you use custom styles: right-to-left lines are laid out left to right and then +aligned right, and characters outside the basic range, such as most emoji, don't draw. + +## Copy styles to another resume + +Section types, element names and field names work in any resume. Rules that use an entry's `id` only match that +entry, and template parts only exist in their template. + +1. In the source resume, select **Copy stylesheet**. +2. In the other resume, open **Design → Advanced → Custom Styles** and paste. +3. Remove or rewrite rules with `item[id=...]` selectors, and template-part rules for a different template. +4. Check the page and the downloaded PDF. + +## Reference + +### Target the right part of your resume + +Semantic CSS selectors describe resume content, not a template's internal layout. Element names and attribute names +are lowercase and case-sensitive. + +**Resume structure** + +| Element | Targets | Typical use | +| --- | --- | --- | +| `resume` | The whole resume | Scope a rule to one template. | +| `page` | One PDF page | Set a page size. | +| `region` | The header, main, sidebar or featured area of a page | Style a layout area. | +| `header` | The resume header | Style the name and contact area. | +| `section` | A section | Target a section type or placement. | +| `section-heading` | A section's title | Change heading type or add a rule under it. | +| `section-items` | The entries in a section | Adjust entry layout and gaps. | +| `item` | One entry | Space or keep together a job, project or similar entry. | +| `item-header` | An entry's top row | Align the title, company, dates or similar details. | + +**Header and entry content** + +| Element | Targets | +| --- | --- | +| `picture` | The profile picture. | +| `name`, `headline` | The name and headline in the header. | +| `contact-list`, `contact-item` | The contact details in the header. | +| `combined-text` | A value the template builds from several fields. | +| `field` | A named field, such as a position, company or date. | +| `link` | A link. | +| `icon`, `level` | An icon, or a skill or language level indicator. | + +**Rich text and lists** + +| Element | Targets | +| --- | --- | +| `rich-text`, `rich-heading`, `blockquote`, `paragraph` | Formatted text blocks in descriptions and the summary. | +| `list`, `list-item`, `list-marker`, `list-item-content` | A list, one list row, its bullet or number, and its text. | +| `strong`, `emphasis`, `underline`, `strike`, `code`, `text-span`, `mark` | Inline formatting. | +| `hard-break`, `horizontal-rule` | A line break or horizontal rule. | +| `template-part` | A template-specific detail. Always guard it with a template (see below). | ### Narrow a selector with attributes -Use attributes to make a rule specific without relying on a template layout. - | Attribute | Use it with | Example | | --- | --- | --- | -| `type` | `section` | `section[type="experience"]` | -| `placement` | `region` and `section` | `region[placement="sidebar"]` | -| `region` | `region` | `region[region="sidebar"]` | +| `type` | `section`, `icon` | `section[type="experience"]` | +| `placement` | `region`, `section` | `region[placement="sidebar"]` | +| `region` | `region`, `header` | `region[region="featured"]` | | `origin` | `section` | `section[origin="main"]` | -| `part` | `region`, `section`, `contact-item`, and `item-header` | `region[part~="sidebar-background"]` | +| `part` | `region`, `section`, `contact-item`, `item-header` | `region[part~="sidebar-background"]` | | `template` | `resume` | `resume[template="azurill"]` | -| `name` | `field` and `template-part` | `field[name="position"]` | +| `name` | `field`, `contact-item`, `combined-text`, `template-part` | `field[name="position"]` | | `level` | `rich-heading` | `rich-heading[level="2"]` | | `direction` | `list-item-content` | `list-item-content[direction="rtl"]` | -| `id` | Any semantic node when present | `section[id="projects"]` | -| `role` | Any semantic node when present | `field[role~="secondary-text"]` | +| `page-number` | `page` | `page[page-number="1"]` | +| `id` | Any element that has one | `section[id="projects"]` | +| `role` | Any element with roles | `field[role~="secondary-text"]` | -Semantic CSS supports selector lists, descendant (` `), child (`>`), adjacent sibling (`+`), and general sibling (`~`) -combinators. It also supports `:root`, `:first-child`, `:last-child`, `:only-child`, `:is()`, `:where()`, `:not()`, -`:nth-child()`, and `:nth-of-type()`. +Attribute matchers `=`, `~=`, `^=` and `*=` are supported. `#projects` is shorthand for `[id="projects"]` when the ID +is a plain word (section IDs such as `experience` are; entry IDs aren't, so use `item[id="..."]`). + +Selectors can be combined with commas, and with the descendant (space), child (`>`), next-sibling (`+`) and +later-sibling (`~`) combinators. Supported pseudo-classes: `:root`, `:first-child`, `:last-child`, `:only-child`, +`:nth-child()`, `:nth-of-type()`, `:is()`, `:where()` and `:not()`. `:nth-of-type` counts elements of the same kind, +so `item:nth-of-type(1)` is the first entry. ```css section[type="experience"] > section-heading { @@ -155,18 +242,15 @@ region[placement="sidebar"] { padding: 18pt; } -section[id="projects"] { - break-inside: avoid; +section[type="experience"] item:nth-of-type(1) { + margin-bottom: 12pt; } ``` -Use an exact `id` only for a resume-specific adjustment. A type, placement, role, or field name is usually a better -choice when you expect to copy the stylesheet to another resume. +### Reuse your Design settings -## Reuse your builder settings - -Semantic CSS exposes the resolved builder settings as read-only `--resume-*` variables. Define your own variables in `:root`, then -reuse the builder values instead of duplicating colors or dimensions. +Your Design settings are available as read-only `--resume-*` variables. Define your own variables in `:root` and +reuse the resume's values, so your styles follow when you change a color or size in Design. ```css :root { @@ -177,41 +261,38 @@ reuse the builder values instead of duplicating colors or dimensions. section-heading { color: var(--accent); border-bottom: 1pt solid var(--rule); - font-size: 11pt; - font-weight: 600; letter-spacing: 0.4pt; } ``` -Changing the primary color or related setting in the builder updates the corresponding variable automatically. Do not -assign a value to a `--resume-*` variable; create an author variable such as `--accent` instead. +Don't assign a value to a `--resume-*` variable; create your own, such as `--accent`, instead. -| Builder setting | Read-only variables | +| Design setting | Read-only variables | | --- | --- | | Colors | `--resume-primary-color`, `--resume-text-color`, `--resume-background-color` | -| Typography | `--resume-body-font-size`, `--resume-body-line-height`, `--resume-heading-font-size`, `--resume-heading-line-height` | +| Type | `--resume-body-font-size`, `--resume-body-line-height`, `--resume-heading-font-size`, `--resume-heading-line-height` | | Page and layout | `--resume-page-gap-x`, `--resume-page-gap-y`, `--resume-page-margin-x`, `--resume-page-margin-y`, `--resume-page-width`, `--resume-page-height`, `--resume-sidebar-width` | | Picture | `--resume-picture-size`, `--resume-picture-rotation`, `--resume-picture-aspect-ratio`, `--resume-picture-border-radius`, `--resume-picture-border-width`, `--resume-picture-border-color`, `--resume-picture-shadow-width`, `--resume-picture-shadow-color` | -Use `pt` for predictable PDF spacing and type sizes. Semantic CSS also accepts `px`, `in`, `mm`, `cm`, `%`, `em`, and `rem` -where the property supports a length. +Use `pt` for predictable sizes in a PDF. Lengths also accept `px`, `in`, `mm`, `cm`, `em` and `rem`, and `%` where the +[engine limits](#engine-limits) allow it. `inherit` and `initial` work as values, and `!important` is respected. -## Style common resume content +### Properties -The most useful declarations usually fall into a few groups: - -| Goal | Common declarations | +| Goal | Properties | | --- | --- | -| Typography | `color`, `font-size`, `font-style`, `font-weight`, `letter-spacing`, `line-height`, `text-align`, `text-decoration`, `text-transform` | -| Spacing and layout | `margin`, `padding`, `gap`, `width`, `height`, `display`, `flex`, `flex-direction`, `justify-content`, `align-items`, `order` | -| Visual treatment | `background-color`, `border`, `border-radius`, `opacity`, `transform` | -| Picture treatment | `object-fit`, `object-position`, `-resume-shadow-color`, `-resume-shadow-width` | -| PDF structure | `break-before`, `break-inside`, `orphans`, `widows`, `-resume-min-presence-ahead`, `size` | +| Text | `color`, `font-size`, `font-style`, `font-weight`, `letter-spacing`, `line-height`, `text-align`, `text-decoration`, `text-transform`, `text-overflow`, `direction` | +| Spacing and size | `margin`, `padding` (and their side forms), `gap`, `row-gap`, `column-gap`, `width`, `height`, `min-width`, `max-width`, `min-height`, `max-height`, `aspect-ratio` | +| Layout | `display` (`flex` or `none`), `flex`, `flex-direction`, `flex-wrap`, `justify-content`, `align-items`, `align-self`, `order`, `position`, `top`, `right`, `bottom`, `left`, `overflow` | +| Look | `background-color`, `border` (and its side, width, style, color and radius forms), `opacity`, `transform`, `transform-origin` | +| Picture | `object-fit`, `-resume-shadow-color`, `-resume-shadow-width` | +| Page breaks | `break-before`, `break-inside`, `orphans`, `widows`, `size` (on `page` only) | -Use `display: none` only to hide an existing semantic node. Semantic CSS cannot add, remove, duplicate, or re-parent resume -data. +`background-color` and borders apply to container elements (such as `header`, `section`, `item`, `region`), not to +text elements. `display: none` hides an element that's already there; to remove content, hide it in **Write** instead, +so it's also left out of exports. -Semantic CSS keeps background styling PDF-safe. Use a flat color for headers and regions: +Backgrounds must be flat colors. Gradients such as `linear-gradient(...)` aren't supported: ```css header { @@ -219,13 +300,9 @@ header { } ``` -Gradient declarations such as `background-image: linear-gradient(...)` remain unsupported; use `background-color` instead. -An unsupported declaration is left out while the declarations around it still apply. +### Style common details -### Style rich-text lists - -`list-item` is the outer row that holds a marker and its content. Use it for row layout and spacing. Use `list-marker` -for the bullet or number, and `list-item-content` for the text flow. +List rows, markers and text: ```css rich-text list-item { @@ -241,9 +318,7 @@ list-item-content { } ``` -### Space level indicators - -Target `level` to adjust the space between a skill's circles, icons, or other level decorations: +Space between a skill's level icons (`gap` works too; `row-gap` doesn't change this single row): ```css section[type="skills"] level { @@ -251,13 +326,7 @@ section[type="skills"] level { } ``` -This sets a 4pt horizontal gap between decorations. `gap: 4pt` also works; `gap: 0` removes the gap. -`row-gap` does not change horizontal spacing within the single level row. - -### Style fields inside an item - -Named fields let you make a focused change without styling every item value. Use the selector only where that field -exists in the selected resume and template. +Named fields inside entries: ```css section[type="experience"] field[name="position"] { @@ -269,49 +338,18 @@ section[type="experience"] field[name="company"] { } ``` -## Use template-specific parts carefully +### Control page breaks and page size -Template parts expose optional visual details that are not shared by every template. Always guard a template-part rule -with `resume[template="..."]`; otherwise the selector may match nothing after a template change. - -```css -resume[template="azurill"] template-part[name="timeline-line"] { - background-color: #94a3b8; -} -``` - -Some template parts are wrappers, while others are attributes on an existing semantic node. Use the matching selector -below. - -| Template | Available selectors | -| --- | --- | -| Azurill | `template-part[name="timeline-content"]`, `template-part[name="timeline-dot"]`, `template-part[name="timeline-line"]`, `template-part[name="timeline-marker"]` | -| Bronzor | `section[part~="interleaved-section-row"]` | -| Chikorita | `template-part[name="contact-row-primary"]`, `template-part[name="contact-row-secondary"]` | -| Ditgar | `template-part[name="featured-summary"]`, `item-header[part~="item-header-border"]`, `region[part~="sidebar-background"]` | -| Ditto | `template-part[name="contact-offset"]`, `template-part[name="header-band"]`, `template-part[name="picture-anchor"]` | -| Gengar | `template-part[name="featured-summary"]`, `region[part~="sidebar-background"]` | -| Glalie | `region[part~="sidebar-background"]` | -| Leafish | `template-part[name="header-body"]`, `template-part[name="header-contact-band"]`, `template-part[name="header-intro"]` | -| Meowth | `template-part[name="education-grade-row"]`, `template-part[name="inline-item-header-leading"]`, `template-part[name="inline-item-header-middle"]`, `template-part[name="inline-item-header-trailing"]` | -| Pikachu | `template-part[name="header-divider"]` | -| Rhyhorn | `template-part[name="contact-item-content"]`, `contact-item[part~="contact-item-last"]` | -| Scizor | `template-part[name="header-name-rule"]` | - -Kakuna, Lapras, and Onyx do not expose template-specific parts. Use shared semantic selectors for portable styles. - -## Control pagination and PDF dimensions - -Use structural declarations sparingly and review the exported PDF after each change. You can keep an item together, -leave space before a section, or set a custom page size. +Start a section on a new page, keep entries whole, or set a custom page size. Review the downloaded PDF after each +change. ```css page { size: 210mm 297mm; } -section { - -resume-min-presence-ahead: 72pt; +section[type="projects"] { + break-before: page; } item { @@ -319,8 +357,10 @@ item { } ``` -`size` applies only to `page` and must be outside `@media`. PDF media queries use the authored PDF dimensions, not the -browser viewport. +`size` takes `A4`, `letter`, or a width and height. It applies only to `page` and can't be inside `@media`. + +`@media` queries use the PDF page's dimensions, not your browser window. Supported features are `width`, `min-width`, +`max-width`, `height`, `min-height`, `max-height` and `orientation` (`portrait` or `landscape`). ```css @media (max-width: 600pt) { @@ -330,41 +370,46 @@ browser viewport. } ``` -Supported media features are `width`, `min-width`, `max-width`, `height`, `min-height`, `max-height`, and -`orientation: portrait` or `orientation: landscape`. +### Template-specific parts -## Diagnose and recover safely +Some templates expose extra details that others don't have. Always guard these rules with `resume[template="..."]`, so +they don't apply by accident after a template change. -The editor saves whatever you write. Anything it can't apply (an unknown property, a value it doesn't accept, a selector -that doesn't parse) is left out, and everything else still appears in the preview and PDF export. If you see no change, -the rule didn't apply. +```css +resume[template="azurill"] template-part[name="timeline-line"] { + background-color: #94a3b8; +} +``` -If a rule does not work: +| Template | Selectors | +| --- | --- | +| Azurill | `template-part[name="timeline-content"]`, `template-part[name="timeline-dot"]`, `template-part[name="timeline-line"]`, `template-part[name="timeline-marker"]` | +| Bronzor | `section[part~="interleaved-section-row"]` | +| Chikorita | `template-part[name="contact-row-primary"]`, `template-part[name="contact-row-secondary"]` | +| Ditgar | `template-part[name="featured-summary"]`, `item-header[part~="item-header-border"]`, `region[part~="sidebar-background"]` | +| Ditto | `template-part[name="contact-offset"]`, `template-part[name="header-band"]`, `template-part[name="picture-anchor"]` | +| Gengar | `template-part[name="featured-summary"]`, `region[part~="sidebar-background"]` | +| Glalie | `template-part[name="sidebar-background"]` | +| Leafish | `template-part[name="header-body"]`, `template-part[name="header-contact-band"]`, `template-part[name="header-intro"]` | +| Meowth | `template-part[name="education-grade-row"]`, `template-part[name="inline-item-header-leading"]`, `template-part[name="inline-item-header-middle"]`, `template-part[name="inline-item-header-trailing"]` | +| Pikachu | `template-part[name="header-divider"]` | +| Rhyhorn | `template-part[name="contact-item-content"]`, `contact-item[part~="contact-item-last"]` | +| Scizor | `template-part[name="header-name-rule"]` | -1. Check the selector's spelling, attribute value, placement, and template guard. A selector that matches nothing usually - means the resume does not contain that semantic node. -2. Simplify the rule to one selector and one declaration, then add more once it shows in the preview. -3. Use the stylesheet undo and redo controls to restore an earlier source. +Kakuna, Lapras and Onyx have no template-specific parts. -Select **Open focus mode** when you need a taller editor. On mobile, it opens a full-width sheet; switch to -**Preview** to inspect the result. +Every template also has `template-part[name="item-header-row"]`: the row with the title and date in Awards, +Certifications, Projects and Publications entries. It doesn't need a template guard. - - Review the PDF preview before exporting or sharing a resume with Custom Styles. PDF pagination and template-specific - details can make a valid stylesheet look different from what you intended. - +### Not supported -## Keep styles portable +Classes, pseudo-elements (`::before`, `::after`), CSS Grid, `@import`, `@font-face` and other at-rules except +`@media`, `url()` and any external file, gradients, general box shadows, filters, animations and transitions. Use the +Design controls for fonts, the picture and broader layout changes. -When you copy a stylesheet to another resume, semantic section types, placements, roles, and fields are the safest -starting point. Exact IDs and template parts are intentionally specific to a resume or template. +## Related guides -1. Select **Copy stylesheet** in the source resume. -2. Open **Design → Custom Styles** in the destination resume. -3. Paste the stylesheet. -4. Replace or remove exact IDs and template-part rules that do not apply. -5. Compare the preview and exported PDF. - -Semantic CSS does not support classes, pseudo-elements, CSS Grid, arbitrary at-rules, `@import`, `@font-face`, `url()`, -browser APIs, animations, filters, gradients, general box shadows, or external assets. Use the normal builder settings -when you need a font, image, or broader layout change. +- [Choosing a template](/guides/choosing-a-template): a different template may get you most of the way without CSS. +- [Customizing typography](/guides/customizing-typography): fonts, sizes and line height from Design. +- [Arranging the layout](/guides/arranging-the-layout): columns, sidebar and page breaks without CSS. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): get back to a stylesheet that worked. diff --git a/docs/community/sponsors.mdx b/docs/community/sponsors.mdx index ac6450c22..d452c9f71 100644 --- a/docs/community/sponsors.mdx +++ b/docs/community/sponsors.mdx @@ -1,13 +1,14 @@ --- title: Sponsors -description: "The sponsors whose support funds ongoing development and hosting of Reactive Resume." +description: "The sponsors whose support funds ongoing development and hosting of Reactive Resume, and how you or your company can sponsor the project." --- Sponsors pay for hosting, maintenance, and ongoing development, which is what keeps Reactive Resume free and independent. Thank you to everyone who chips in. ## Atlas Cloud -Atlas Cloud +Atlas Cloud +Atlas Cloud [Atlas Cloud](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=reactive-resume) supports Reactive Resume as a project sponsor. Atlas Cloud provides a unified AI platform for developers, with access to hundreds of models for chat, image generation, video generation, media processing, and GPU cloud workloads through one API key, one endpoint, and one billing account. @@ -15,4 +16,6 @@ Learn more at [atlascloud.ai](https://www.atlascloud.ai/?utm_source=github&utm_m ## Sponsor Reactive Resume +If Reactive Resume has helped you, you can support its development through [GitHub Sponsors](https://github.com/sponsors/AmruthPillai) or [Open Collective](https://opencollective.com/reactive-resume/donate). + If your company would like to sponsor Reactive Resume, email [hello@amruthpillai.com](mailto:hello@amruthpillai.com). diff --git a/docs/comparisons/reactive-resume-vs-adobe-express.mdx b/docs/comparisons/reactive-resume-vs-adobe-express.mdx index 037097acc..3e7595770 100644 --- a/docs/comparisons/reactive-resume-vs-adobe-express.mdx +++ b/docs/comparisons/reactive-resume-vs-adobe-express.mdx @@ -12,8 +12,8 @@ Adobe Express has a resume maker inside a general creative editor. Reactive Resu | Consideration | Reactive Resume | Adobe Express | | --- | --- | --- | | Primary workflow | Resume-specific fields and templates | General visual editing with resume templates | -| Editing scope | Resume layout, typography, colors, spacing, and sections | Document, image, layout, and graphic editing | -| Exports | PDF, DOCX, Markdown, and Reactive Resume JSON | Resume creation and export options in Adobe Express | +| Editing scope | 15 resume templates, typography, accent colour, page layout, and sections | Document, image, layout, and graphic editing | +| Exports | PDF, Word (DOCX), Markdown, and Reactive Resume JSON | Resume creation and export options in Adobe Express | | Content reuse | Structured data can be reused across resume versions | Reuse designs and assets in the Adobe Express workflow | | Deployment | Hosted use or self-hosting under an MIT license | Adobe-hosted service | | Free plan | Core hosted resume workflow has no premium tier | Free-plan features and asset access have defined limits | @@ -28,7 +28,7 @@ The wider editor is the real advantage. It handles work that falls outside a res ## Where Reactive Resume is a better fit -Use Reactive Resume when work history and other sections should stay structured rather than sit on the page as arranged elements. That makes it easier to prepare targeted versions, switch templates, or keep a data backup next to your document exports. The [exporting guide](/guides/exporting-your-resume) covers PDF, DOCX, Markdown, and JSON. +Use Reactive Resume when work history and other sections should stay structured rather than sit on the page as arranged elements. That makes it easier to copy a resume for each job, switch templates, write a matching cover letter that reuses the resume's design, or keep a data backup next to your document exports. The [exporting guide](/guides/exporting-your-resume) covers PDF, DOCX, Markdown, and JSON. There are also options beyond the hosted app. The project is [open source](/use-cases/open-source-resume-builder) under MIT and supports [self-hosting](/use-cases/self-hosted-resume-builder). Those only matter if you need them; for a single visual document, Adobe Express is the more direct editor. diff --git a/docs/comparisons/reactive-resume-vs-canva.mdx b/docs/comparisons/reactive-resume-vs-canva.mdx index a22ca465e..1cecc6844 100644 --- a/docs/comparisons/reactive-resume-vs-canva.mdx +++ b/docs/comparisons/reactive-resume-vs-canva.mdx @@ -12,8 +12,8 @@ Both tools make resumes, but they start from different models. Canva is a genera | Consideration | Reactive Resume | Canva | | --- | --- | --- | | Primary workflow | Structured resume sections with a live preview | Freeform visual editing across many design types | -| Design controls | Resume templates, colors, typography, layout, and spacing | Flexible composition, elements, and a broad design library | -| Exports | PDF, DOCX, Markdown, and Reactive Resume JSON | See Canva's export options and template terms | +| Design controls | 15 resume templates, font pairings or about 500 fonts, accent colour, density, margins, and custom CSS | Flexible composition, elements, and a broad design library | +| Exports | PDF, Word (DOCX), Markdown, and Reactive Resume JSON | See Canva's export options and template terms | | Data portability | JSON export can back up or restore a resume | Depends on the design and export format you choose | | Deployment | Hosted use or self-hosting under the MIT license | Canva-hosted service | | Plan boundary | The hosted core resume workflow has no premium tier | Some Canva content and features have plan-specific availability | @@ -28,7 +28,7 @@ Canva is also the convenient option when you are preparing related design materi ## Where Reactive Resume is a better fit -Choose Reactive Resume when the content should stay in recognizable resume fields and you expect to reuse it across versions. You can change a template without re-entering the underlying sections, adjust resume layout settings, and export in several document and data formats. +Choose Reactive Resume when the content should stay in recognizable resume fields and you expect to reuse it across versions. You can change between 15 templates without re-entering the underlying sections, adjust layout settings, and export in several document and data formats. Cover letters are their own documents that share the resume's design, and Check mode flags lines that applicant tracking systems may read poorly. It also fits when you care about infrastructure. Reactive Resume is [MIT-licensed](/legal/license), its source is public, and you can deploy it yourself. The [self-hosting guide](/self-hosting/docker) covers the Docker path, and hosted use stays available if you would rather not run the service. diff --git a/docs/comparisons/reactive-resume-vs-careercircle.mdx b/docs/comparisons/reactive-resume-vs-careercircle.mdx index 393f58fe3..1c9bdb7a8 100644 --- a/docs/comparisons/reactive-resume-vs-careercircle.mdx +++ b/docs/comparisons/reactive-resume-vs-careercircle.mdx @@ -9,15 +9,15 @@ CareerCircle puts resume drafting next to job listings, courses, and professiona | Consideration | Reactive Resume | CareerCircle | | --- | --- | --- | -| Resume workflow | Structured resume editor with reusable versions | Resume builder within a career-services platform | -| Downloads | PDF, DOCX, Markdown, and JSON | PDF and Microsoft Word downloads | -| Writing support | Direct editing and optional AI | Guidance and tips from staffing experts | -| Career services | Resume and application tools | Jobs, courses, and professional-development resources | -| Account | An account stores resume versions | Sign-up is required to use the service | +| Resume workflow | Structured resume and cover-letter editor with a copy per job | Resume builder within a career-services platform | +| Downloads | PDF, Word (DOCX), Markdown, and JSON | PDF and Microsoft Word downloads | +| Writing support | Direct editing, Check mode, and an optional AI assistant using your own provider | Guidance and tips from staffing experts | +| Career services | Job application tracker with board, calendar, and insights views; no job listings or courses | Jobs, courses, and professional-development resources | +| Account | An account stores your documents and applications | Sign-up is required to use the service | CareerCircle documents PDF and Microsoft Word downloads from its resume builder. It puts staffing-expert guidance, job search, courses, and professional-development resources in the same service. -Reactive Resume supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume), and its [dashboard](/guides/managing-resumes-from-the-dashboard) keeps separate resume versions. Its source is public under the [MIT license](/legal/license), with a Docker self-hosting path. +Reactive Resume supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume), and its [Documents page](/guides/managing-documents) keeps your resumes and cover letters side by side. Its [application tracker](/guides/tracking-job-applications) follows each job from saved to offer. Its source is public under the [MIT license](/legal/license), with a Docker self-hosting path. ## Where CareerCircle is a better fit @@ -25,7 +25,7 @@ Choose CareerCircle when you want the resume builder connected to job search, co ## Where Reactive Resume is a better fit -Pick Reactive Resume to keep several resume versions and export them in document or data formats. You can also run the software yourself; see the [self-hosting guide](/self-hosting/docker). +Pick Reactive Resume to keep a resume for each job, track where you applied, and export in document or data formats. You can also run the software yourself; see the [self-hosting guide](/self-hosting/docker). ## Which should you choose? @@ -33,7 +33,7 @@ Choose CareerCircle for a sign-up-based career service with its resume builder, ## Reactive Resume limitations in this comparison -Reactive Resume has none of CareerCircle's course, job, and community services. If those resources should sit alongside the resume workflow, CareerCircle is the closer fit. +Reactive Resume has none of CareerCircle's course, job-listing, and community services. Its tracker records jobs you find elsewhere; it does not find jobs for you. If those resources should sit alongside the resume workflow, CareerCircle is the closer fit. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-freesumes.mdx b/docs/comparisons/reactive-resume-vs-freesumes.mdx index d02472c97..96d40585b 100644 --- a/docs/comparisons/reactive-resume-vs-freesumes.mdx +++ b/docs/comparisons/reactive-resume-vs-freesumes.mdx @@ -9,15 +9,15 @@ Freesumes is built for making one resume in a browser session, without an accoun | Consideration | Reactive Resume | Freesumes | | --- | --- | --- | -| Account | Account-backed resume management | No account or credit card for the builder | -| Builder templates | Select a template for each saved resume | Six current builder templates | +| Account | Account-backed resumes, cover letters, and applications | No account or credit card for the builder | +| Builder templates | 15 templates; switch at any time without retyping | Six current builder templates | | PDF | PDF export | PDF download | -| Other template formats | DOCX, Markdown, and JSON exports | Separate Word and Google Docs templates | +| Other template formats | Word (DOCX), Markdown, and JSON exports | Separate Word and Google Docs templates | | Data handling | Resumes persist in the account and server storage | Builder data is wiped when the tab is refreshed or closed | Freesumes documents a no-account, no-card builder with six templates and PDF output. It offers Word and Google Docs templates separately. Its builder says it does not collect or store what you enter, and that the data is wiped when you refresh or close the browser tab. -Reactive Resume keeps resume versions in its [dashboard](/guides/managing-resumes-from-the-dashboard), with [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). It can also [share resumes](/use-cases/export-and-share-resumes) and run API or MCP automation. +Reactive Resume keeps your resumes and cover letters on its [Documents page](/guides/managing-documents), with [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). It can also [share resumes](/use-cases/export-and-share-resumes) through password-protected links, track job applications, and run API or MCP automation. ## Where Freesumes is a better fit @@ -25,7 +25,7 @@ Choose Freesumes for a one-off browser session without an account or card, if yo ## Where Reactive Resume is a better fit -Reactive Resume holds on to your resume versions so you can share them or export them in several formats. It can also be self-hosted; see the [Docker guide](/self-hosting/docker). +Reactive Resume holds on to your resumes, with version history, so you can come back to them, share them, or export them in several formats. It can also be self-hosted; see the [Docker guide](/self-hosting/docker). ## Which should you choose? @@ -33,7 +33,7 @@ Choose Freesumes for a no-account, browser-session PDF workflow or its separate ## Reactive Resume limitations in this comparison -Reactive Resume needs an account and stores resumes on the server. That is a drawback if you want a one-off local browser session where the builder data disappears on refresh or close. +Reactive Resume's editor needs an account and stores resumes on the server. That is a drawback if you want a one-off local browser session where the builder data disappears on refresh or close. Only its public [ATS checker](/guides/using-the-ats-checker) works without an account, and it checks an existing PDF rather than building one. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-jobscan.mdx b/docs/comparisons/reactive-resume-vs-jobscan.mdx index 7f2609953..4352c5ad6 100644 --- a/docs/comparisons/reactive-resume-vs-jobscan.mdx +++ b/docs/comparisons/reactive-resume-vs-jobscan.mdx @@ -3,20 +3,20 @@ title: "Reactive Resume vs Jobscan" description: "Compare Reactive Resume and Jobscan by free PDF creation, ATS parse checking, job matching, exports, open source, and automation." --- -Jobscan makes sense when your workflow revolves around its hosted resume scanner and the match rate it reports against a job description. Reactive Resume gives you structured resume data, several export formats, open-source deployment, and API or MCP automation, plus a free ATS checker that measures how well your file parses rather than predicting an outcome. +Jobscan makes sense when your workflow revolves around its hosted resume scanner and the match rate it reports against a job description. Reactive Resume gives you structured resume data, several export formats, open-source deployment, and API or MCP automation, plus free checks that measure how well your file parses rather than predicting an outcome. ## Quick comparison | Consideration | Reactive Resume | Jobscan | | --- | --- | --- | -| Resume creation | Structured editor with PDF, DOCX, Markdown, and JSON exports | Free builder with nine current templates and PDF download | -| Import | Supported resume imports | Existing-resume and LinkedIn import | -| Analysis | Built-in ATS checker, free: a parse-quality score, a pass/warn/fail checklist, and optional job-description keyword coverage, all computed in your browser | Separate resume scanner producing a match rate against a job description | -| Automation | Authenticated API and MCP workflows | Separate scanner workflow | +| Resume creation | Structured editor with 15 templates and PDF, Word (DOCX), Markdown, and JSON exports | Free builder with nine current templates and PDF download | +| Import | PDF, Word (with your own AI provider), LinkedIn data export, JSON Resume, and Reactive Resume files | Existing-resume and LinkedIn import | +| Analysis | Free, in two places: Check mode in the editor (readability score, issues pinned to lines, posting terms you have and lack) and a public ATS checker that reads a PDF in your browser | Separate resume scanner producing a match rate against a job description | +| Automation | Authenticated API and MCP workflows, including job applications | Separate scanner workflow | Jobscan's builder documents a free PDF-creation workflow, LinkedIn import, and nine ATS-friendly templates. Its separate scanner lists resume scoring, formatting checks, and job-listing analysis as product features. Those are vendor claims about the product, not guarantees about ATS handling or hiring outcomes. -Reactive Resume exports PDF, DOCX, Markdown, and JSON, and it supports authenticated automation through its API and MCP server. Its [ATS checker](/guides/using-the-ats-checker) scores how faithfully software can extract your PDF and lists which of a posting's terms already appear in it. It reports no match rate and predicts no rejection, because neither is knowable from the file. +Reactive Resume exports PDF, DOCX, Markdown, and JSON, and it supports authenticated automation through its API and MCP server. Its public [ATS checker](/guides/using-the-ats-checker) scores how faithfully software can extract your PDF, shows the text as software reads it, and lists which of a posting's terms already appear in it. Inside the editor, [Check mode](/guides/checking-your-resume) runs the same kind of checks live as you type, pins each issue to its line on the page, and matches your resume against the posting saved with a job application. Neither reports a match rate or predicts a rejection, because neither is knowable from the file. ## Where Jobscan is a better fit @@ -24,7 +24,7 @@ Choose Jobscan when a match rate against a specific posting, and the recommendat ## Where Reactive Resume is a better fit -Reactive Resume is the better choice when you want to own structured resume data, pick a template, export in several formats, or automate authenticated resume workflows. Its API and MCP server support scripts, integrations, and compatible AI clients. +Reactive Resume is the better choice when you want to own structured resume data, pick a template, fix issues where they appear on the page, track your applications, export in several formats, or automate authenticated resume workflows. Its API and MCP server support scripts, integrations, and compatible AI clients. ## Which should you choose? @@ -32,13 +32,14 @@ Choose Jobscan when a match rate against each posting is your primary need. Choo ## Reactive Resume limitations in this comparison -Reactive Resume's ATS checker measures file properties: whether the text extracts, in what order, and whether your facts survive. It produces no match rate against a posting and makes no claim about predicting a rejection. If you want a documented match-rate score, Jobscan is the more direct fit. +Reactive Resume's checks measure file properties: whether the text extracts, in what order, and whether your facts survive. Job match lists which posting terms you have and lack, but produces no match rate and makes no claim about predicting a rejection. If you want a documented match-rate score, Jobscan is the more direct fit. ## Sources - [Jobscan resume builder](https://www.jobscan.co/resume-builder) - [Jobscan resume scanner](https://www.jobscan.co/resume-scanner) - [Reactive Resume: using the ATS checker](/guides/using-the-ats-checker) +- [Reactive Resume: checking your resume](/guides/checking-your-resume) - [Reactive Resume: exporting your resume](/guides/exporting-your-resume) - [Reactive Resume: using the API](/guides/using-the-api) - [Reactive Resume: using the MCP server](/guides/using-the-mcp-server) diff --git a/docs/comparisons/reactive-resume-vs-kickresume.mdx b/docs/comparisons/reactive-resume-vs-kickresume.mdx index 2e0d69009..2f16e10a5 100644 --- a/docs/comparisons/reactive-resume-vs-kickresume.mdx +++ b/docs/comparisons/reactive-resume-vs-kickresume.mdx @@ -9,15 +9,15 @@ Kickresume bundles AI writing, imports, examples, and related career documents i | Consideration | Reactive Resume | Kickresume | | --- | --- | --- | -| AI | Optional; connect and configure your own provider | Integrated AI writing and rewriting tools | -| Content help | Direct editing and AI assistance after provider setup | Examples, guides, and AI-generated drafts | -| Imports | Supported resume imports | LinkedIn and PDF/DOCX import paths | -| Documents | Resume-focused exports and versions | Resumes, cover letters, websites, and related tools | +| AI | Optional assistant that proposes edits you accept one by one; connect your own provider | Integrated AI writing and rewriting tools | +| Content help | Direct editing, Check mode issues, and AI assistance after provider setup | Examples, guides, and AI-generated drafts | +| Imports | PDF, Word (with your own AI provider), LinkedIn data export, JSON Resume, and Reactive Resume files | LinkedIn and PDF/DOCX import paths | +| Documents | Resumes and cover letters, a copy per job, and a job application tracker | Resumes, cover letters, websites, and related tools | | Free plan | Core hosted workflow has no premium tier | Unlimited documents and downloads when using free customization options | Kickresume's help center documents AI writing, LinkedIn import, PDF import, examples, guides, and cover-letter and website workflows. Its free-plan FAQ says documents and downloads are unlimited when you use free customization options; premium-marked options fall outside that. -Reactive Resume exports PDF, DOCX, Markdown, and JSON. Its AI stays optional: you connect a provider, a model, an endpoint when one is needed, and an API key. The provider may charge you separately. +Reactive Resume exports PDF, DOCX, Markdown, and JSON, and cover letters are separate documents that share the resume's design. Its AI stays optional: you connect a provider, a model, an endpoint when one is needed, and an API key. The provider may charge you separately. ## Where Kickresume is a better fit @@ -25,7 +25,7 @@ Choose Kickresume when you want prewritten examples, guided prompts, built-in AI ## Where Reactive Resume is a better fit -Reactive Resume is the better choice when you want AI to stay optional and want to pick the provider that handles your resume content. It is open source, can be self-hosted, and has API and MCP workflows for authenticated automation. +Reactive Resume is the better choice when you want AI to stay optional and want to pick the provider that handles your resume content. It is open source, can be self-hosted, tracks your job applications, and has API and MCP workflows for authenticated automation. ## Which should you choose? @@ -33,7 +33,7 @@ Choose Kickresume for a bundled writing, examples, and career-document workflow. ## Reactive Resume limitations in this comparison -Reactive Resume has a smaller built-in library of content examples and needs provider setup before its AI features work. It does not bundle Kickresume's wider guided career-document workflow. +Reactive Resume has a smaller built-in library of content examples and needs provider setup before its AI features work. Beyond resumes and cover letters, it does not bundle Kickresume's wider guided career-document workflow, such as personal websites. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-livecareer.mdx b/docs/comparisons/reactive-resume-vs-livecareer.mdx index 57a793906..c3ee817d8 100644 --- a/docs/comparisons/reactive-resume-vs-livecareer.mdx +++ b/docs/comparisons/reactive-resume-vs-livecareer.mdx @@ -3,20 +3,20 @@ title: "Reactive Resume vs LiveCareer" description: "Compare Reactive Resume and LiveCareer by free downloads, guided content, resume checks, templates, open source, and self-hosting." --- -LiveCareer gives you ready-made content, spell-checking, writing tips, and ResumeCheck while you draft. Reactive Resume has no built-in checker; it gives you PDF or DOCX export and access to the source. +LiveCareer gives you ready-made content, spell-checking, writing tips, and ResumeCheck while you draft. Reactive Resume gives you free PDF or DOCX export, a built-in Check mode, and access to the source. ## Quick comparison | Consideration | Reactive Resume | LiveCareer | | --- | --- | --- | -| Free download | PDF, DOCX, Markdown, and JSON | TXT | +| Free download | PDF, Word (DOCX), Markdown, and JSON | TXT | | PDF and Word | Included export formats | Require premium access for unlimited downloads | -| Drafting support | Direct editing and optional AI | Ready-made content, spell-checking, and writing tips | -| Resume review | No native checker product | ResumeCheck identifies common issues | +| Drafting support | Direct editing and an optional AI assistant using your own provider | Ready-made content, spell-checking, and writing tips | +| Resume review | Check mode: readability score, issues pinned to lines, and job-posting terms | ResumeCheck identifies common issues | LiveCareer documents TXT as the free builder's download format, and unlimited PDF and Word downloads under premium access. The product also documents ready-made content, spell-checking, writing tips, and ResumeCheck. -Reactive Resume supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). Its [AI workflow](/guides/using-ai) is optional and provider-configured, and its source is public under the [MIT license](/legal/license). +Reactive Resume supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). Its [Check mode](/guides/checking-your-resume) flags issues as you edit, its [AI features](/guides/using-ai) are optional and provider-configured, and its source is public under the [MIT license](/legal/license). ## Where LiveCareer is a better fit @@ -32,7 +32,7 @@ Choose LiveCareer when its guided content and checker suit how you draft and TXT ## Reactive Resume limitations in this comparison -Reactive Resume has a smaller guided-content and checker surface. It has no equivalent to LiveCareer's ready-made content, spell-checking, writing tips, or its ResumeCheck product. +Reactive Resume has a smaller guided-content surface. It has no equivalent to LiveCareer's ready-made content library or writing tips, and its Check mode focuses on how software reads your resume rather than spelling. An AI writing review is available only after you connect your own provider. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-myperfectresume.mdx b/docs/comparisons/reactive-resume-vs-myperfectresume.mdx index 152657ab3..1a9f76db9 100644 --- a/docs/comparisons/reactive-resume-vs-myperfectresume.mdx +++ b/docs/comparisons/reactive-resume-vs-myperfectresume.mdx @@ -3,7 +3,7 @@ title: "Reactive Resume vs MyPerfectResume" description: "Compare Reactive Resume and MyPerfectResume by free downloads, writing guidance, templates, AI, open source, and self-hosting." --- -MyPerfectResume is built around guided writing: step-by-step prompts, expert-written content suggestions, and a resume checker. Reactive Resume is an open-source editor whose core hosted workflow has no premium tier and includes PDF, DOCX, Markdown, and JSON exports, but it has no equivalent content library or specialized checker. +MyPerfectResume is built around guided writing: step-by-step prompts, expert-written content suggestions, and a resume checker. Reactive Resume is an open-source editor whose core hosted workflow has no premium tier and includes PDF, DOCX, Markdown, and JSON exports and a built-in Check mode, but it has no equivalent library of prewritten content. ## Quick comparison @@ -11,8 +11,8 @@ MyPerfectResume is built around guided writing: step-by-step prompts, expert-wri | --- | --- | --- | | Free final download | PDF, DOCX, Markdown, and JSON | Plain-text TXT | | Designed PDF and Word | Included exports | Require premium access | -| Writing help | Direct editing; optional provider-configured AI | Step-by-step prompts, tips, and content suggestions | -| Checking tools | No equivalent specialized checker | ResumeCheck feedback is listed with premium access | +| Writing help | Direct editing; optional AI assistant using your own provider | Step-by-step prompts, tips, and content suggestions | +| Checking tools | Free Check mode (contact details, dates, layout, headings, job-posting terms) and a public ATS checker | ResumeCheck feedback is listed with premium access | | Deployment | Hosted use or self-hosting | Hosted service | MyPerfectResume's free-builder guide says free accounts get tailored content suggestions, expert tips, and unlimited plain-text downloads, and that PDF or Word template downloads need a premium plan. Its pricing page lists TXT downloads with basic access and puts PDF, Word, and ResumeCheck behind premium access. @@ -27,19 +27,19 @@ Its cover-letter workflow is another reason to pick it if you want guided relate ## Where Reactive Resume is a better fit -Reactive Resume fits when you have the content and want a designed export without picking a premium plan. Make your template and layout choices in the editor, then export a PDF or DOCX for document use, Markdown for text workflows, or JSON for a backup that keeps the structured resume data. The [template selection guide](/guides/choosing-a-template) covers the template workflow. +Reactive Resume fits when you have the content and want a designed export without picking a premium plan. Make your template and layout choices in the editor, fix what Check mode flags, then export a PDF or DOCX for document use, Markdown for text workflows, or JSON for a backup that keeps the structured resume data. A matching cover letter is its own document that reuses the resume's design, with the same free downloads. The [template selection guide](/guides/choosing-a-template) covers the template workflow. You also get control over the application itself. Reactive Resume is [MIT-licensed](/legal/license), its source is public, and you can self-host it. The hosted service stays available if you would rather not run the stack. ## Which should you choose? -Choose MyPerfectResume if you want prompts, prewritten suggestions, and a dedicated resume-checking tool while you draft. Choose Reactive Resume if you already have the content and want designed exports, portable JSON, or the option to run the software yourself. +Choose MyPerfectResume if you want prompts, prewritten suggestions, and its resume-checking tool while you draft. Choose Reactive Resume if you already have the content and want designed exports, portable JSON, or the option to run the software yourself. Neither choice promises an application outcome. The question is whether guided content and checking tools are worth a paid workflow for formatted downloads. ## Reactive Resume limitations in this comparison -Reactive Resume has no equivalent library of role-specific prewritten bullets, no step-by-step writing prompts, and no specialized resume checker. Its optional AI integration asks you to configure a provider and review whatever it suggests. If you want a hosted product that supplies the drafting prompts and the feedback in one place, MyPerfectResume has the broader built-in writing surface. +Reactive Resume has no equivalent library of role-specific prewritten bullets and no step-by-step writing prompts. Its Check mode looks at how software reads your resume, not at what you should write. Its optional AI assistant asks you to configure a provider and review whatever it suggests. If you want a hosted product that supplies the drafting prompts and the feedback in one place, MyPerfectResume has the broader built-in writing surface. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-novoresume.mdx b/docs/comparisons/reactive-resume-vs-novoresume.mdx index 35a092502..b9234454b 100644 --- a/docs/comparisons/reactive-resume-vs-novoresume.mdx +++ b/docs/comparisons/reactive-resume-vs-novoresume.mdx @@ -9,14 +9,14 @@ Novorésumé is the stronger choice when guided design, writing advice, a conten | Consideration | Reactive Resume | Novorésumé | | --- | --- | --- | -| Saved resumes | Manage separate resume versions | Basic permits one document | -| Document length | Use the length your content requires | Basic permits one page; Premium permits longer documents | -| Drafting support | Direct editing and optional AI | Guided design, writing advice, content library, and integrated assistant | -| Exports | PDF, DOCX, Markdown, and JSON | See Novorésumé plan and export options | +| Saved resumes | Unlimited resumes and cover letters | Basic permits one document | +| Document length | As many pages as your content requires | Basic permits one page; Premium permits longer documents | +| Drafting support | Direct editing, Check mode, and an optional AI assistant using your own provider | Guided design, writing advice, content library, and integrated assistant | +| Exports | PDF, Word (DOCX), Markdown, and JSON | See Novorésumé plan and export options | Novorésumé's current Basic plan permits exactly one document, capped at one page. Its Premium plan lists 72 documents and documents up to 10 pages. The product also documents templates, a content library, real-time advice, and an integrated assistant. -Reactive Resume keeps resume versions in the [dashboard](/guides/managing-resumes-from-the-dashboard) and supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). Its source is public under the [MIT license](/legal/license), and it runs with Docker. +Reactive Resume keeps your resumes and cover letters on its [Documents page](/guides/managing-documents), offers 15 templates, and supports [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). Its source is public under the [MIT license](/legal/license), and it runs with Docker. ## Where Novorésumé is a better fit @@ -32,7 +32,7 @@ Choose Novorésumé when its guided drafting tools come first and its Basic or P ## Reactive Resume limitations in this comparison -Reactive Resume has a smaller guidance and content surface. It has no equivalent to Novorésumé's content library and assistant inside a guided drafting workflow. +Reactive Resume has a smaller guidance and content surface. It has no equivalent to Novorésumé's content library or guided drafting workflow, and its AI assistant works only after you connect your own provider. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-overleaf.mdx b/docs/comparisons/reactive-resume-vs-overleaf.mdx index 4e08d3302..9e29fe061 100644 --- a/docs/comparisons/reactive-resume-vs-overleaf.mdx +++ b/docs/comparisons/reactive-resume-vs-overleaf.mdx @@ -12,10 +12,10 @@ The two products use different authoring models. Overleaf is an online LaTeX edi | Consideration | Reactive Resume | Overleaf | | --- | --- | --- | | Primary workflow | Visual resume editing with structured sections | LaTeX source editing and compilation | -| Template model | Built-in resume templates and layout settings | Community CV and resume LaTeX templates | +| Template model | 15 built-in resume templates with font, colour, and layout settings | Community CV and resume LaTeX templates | | Source control | Resume JSON export, not LaTeX source | Direct editing of LaTeX project files | -| Collaboration | Resume sharing and export workflow | Project collaboration with plan-dependent limits | -| Output | PDF, DOCX, Markdown, and Reactive Resume JSON | Compiled document PDFs and project source | +| Collaboration | No shared editing; public links with optional password for readers | Project collaboration with plan-dependent limits | +| Output | PDF, Word (DOCX), Markdown, and Reactive Resume JSON | Compiled document PDFs and project source | | Free plan | Core hosted resume workflow has no premium tier | Free plan has documented collaboration and compile limits | Overleaf's [CV and résumé gallery](https://www.overleaf.com/latex/templates/tagged/cv) collects community templates, and its [plans documentation](https://docs.overleaf.com/getting-started/free-and-premium-plans) explains the differences between free and paid accounts. Reactive Resume keeps the task narrower: enter resume content, pick a template, tune its settings, and export the result. @@ -28,7 +28,7 @@ Overleaf also has a document-collaboration workflow. Its plan pages describe col ## Where Reactive Resume is a better fit -Use Reactive Resume if you prefer a visual editor and do not want to maintain LaTeX source. It keeps common resume content in fields for profile details, experience, education, skills, projects, and other sections, then shows the result in a live preview. You can switch templates and adjust resume layout settings without touching a document class or a compilation config. +Use Reactive Resume if you prefer a visual editor and do not want to maintain LaTeX source. It keeps common resume content in fields for profile details, experience, education, skills, projects, and other sections, then shows the result in a live preview. You can switch templates, fix layout issues that Check mode points out on the page, and adjust layout settings without touching a document class or a compilation config. For finer control, you can add your own CSS under **Advanced** in Design mode. Reactive Resume also has [PDF, DOCX, Markdown, and JSON exports](/guides/exporting-your-resume). The JSON export preserves resume content and settings so you can restore it later or branch another version. If you need to run the software on your own infrastructure, the project is [MIT-licensed](/legal/license) and [self-hostable](/self-hosting/docker). diff --git a/docs/comparisons/reactive-resume-vs-resume-com.mdx b/docs/comparisons/reactive-resume-vs-resume-com.mdx index 3c2e1dc4d..2731bcfa3 100644 --- a/docs/comparisons/reactive-resume-vs-resume-com.mdx +++ b/docs/comparisons/reactive-resume-vs-resume-com.mdx @@ -9,9 +9,9 @@ Reactive Resume and Resume.com both let you create and download a PDF resume wit | Consideration | Reactive Resume | Resume.com | | --- | --- | --- | -| Free final download | PDF, DOCX, Markdown, and JSON | PDF and plain text | +| Free final download | PDF, Word (DOCX), Markdown, and JSON | PDF and plain text | | Accounts | Account required for the hosted builder and saved resume management | An email is requested to save work in the dashboard | -| Workflow | Structured sections, template settings, and live preview | Hosted builder with templates and saved resumes | +| Workflow | Structured sections, 15 templates, live preview, and Check mode | Hosted builder with templates and saved resumes | The common ground here is the download boundary. Resume.com says its builder has no paid membership tier and that PDF and plain-text downloads are free. Reactive Resume is not a cheaper way to get a Resume.com PDF: both produce one without a premium upgrade. @@ -25,7 +25,7 @@ That is the less technical choice if you only need a browser-based editor and a ## Where Reactive Resume is a better fit -Reactive Resume fits when the resume should stay portable as data as well as a document. The editor keeps sections separate from templates, so changing a template does not mean re-entering your work history or education. +Reactive Resume fits when the resume should stay portable as data as well as a document. The editor keeps sections separate from templates, so changing a template does not mean re-entering your work history or education. Cover letters, a job application tracker, and version history sit in the same account. Deployment is your call too. Use the hosted product, read the source, or run it on your own infrastructure. For programmatic access, Reactive Resume exposes [MCP tools](/guides/using-the-mcp-server) and API capabilities. AI assistance is optional and uses a provider you configure; ordinary editing does not need it. diff --git a/docs/comparisons/reactive-resume-vs-resume-io.mdx b/docs/comparisons/reactive-resume-vs-resume-io.mdx index 1aa3103b2..76f5bfc31 100644 --- a/docs/comparisons/reactive-resume-vs-resume-io.mdx +++ b/docs/comparisons/reactive-resume-vs-resume-io.mdx @@ -9,9 +9,9 @@ Resume.io is a commercial, guided builder with sample content and a template-foc | Consideration | Reactive Resume | Resume.io | | --- | --- | --- | -| Free final download | PDF, DOCX, Markdown, and JSON | PDF with the Vancouver template or TXT | -| Other designed templates | Included templates | Premium trial or subscription workflow | -| Content support | Direct editing; optional provider-configured AI | Sample resumes, pre-generated sentences, and tips | +| Free final download | PDF, Word (DOCX), Markdown, and JSON | PDF with the Vancouver template or TXT | +| Other designed templates | All 15 templates included | Premium trial or subscription workflow | +| Content support | Direct editing, Check mode, a sample resume, and an optional AI assistant using your own provider | Sample resumes, pre-generated sentences, and tips | | Deployment | Hosted use or self-hosting | Hosted service | | Pricing availability | No premium resume tier | Plans can vary by location | @@ -27,7 +27,7 @@ The Vancouver template makes it workable for someone who likes that design and n ## Where Reactive Resume is a better fit -Reactive Resume fits when you want to pick from the included templates and export a designed document without a template tier in the way. Its [export guide](/guides/exporting-your-resume) covers PDF, DOCX, Markdown, and JSON. The data export keeps your structured content and settings alongside the document download. +Reactive Resume fits when you want to pick from all 15 templates and export a designed document without a template tier in the way. Cover letters are separate documents with the same design and the same free downloads. Its [export guide](/guides/exporting-your-resume) covers PDF, DOCX, Markdown, and JSON. The data export keeps your structured content and settings alongside the document download. You also get source access and self-hosting. Reactive Resume is [MIT-licensed](/legal/license), and its [Docker guide](/self-hosting/docker) documents the self-hosted route. Its optional AI integration lets you configure a provider if you want help, while ordinary editing does not depend on it. @@ -39,7 +39,7 @@ Both services can make a resume for free. What matters is whether the free downl ## Reactive Resume limitations in this comparison -Reactive Resume has a smaller built-in guided-content library. It has no equivalent to Resume.io's collection of sample resumes, pre-generated cover-letter sentences, and tips. Its optional AI features also require you to choose and configure a provider, which is less convenient if you want a commercial writing workflow already assembled. +Reactive Resume has a smaller built-in guided-content library. It offers one sample resume to start from rather than a collection, and no pre-generated cover-letter sentences or tips. Its optional AI features, including cover-letter drafts from a job posting, also require you to choose and configure a provider, which is less convenient if you want a commercial writing workflow already assembled. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-resume-now.mdx b/docs/comparisons/reactive-resume-vs-resume-now.mdx index a9a549fdd..e0f7c97d0 100644 --- a/docs/comparisons/reactive-resume-vs-resume-now.mdx +++ b/docs/comparisons/reactive-resume-vs-resume-now.mdx @@ -9,10 +9,10 @@ Resume-Now is built around guided drafting, with content suggestions, AI writing | Consideration | Reactive Resume | Resume-Now | | --- | --- | --- | -| Free final download | PDF, DOCX, Markdown, and JSON | Plain-text TXT | +| Free final download | PDF, Word (DOCX), Markdown, and JSON | Plain-text TXT | | Designed PDF and Word | Included exports | Paid access required | -| Writing workflow | Direct fields and optional provider-configured AI | Career questions, suggested bullets, and AI enhancement | -| Analysis tools | No equivalent specialized checker | Resume checker, summary generator, skills generator, and AI review | +| Writing workflow | Direct fields and an optional AI assistant using your own provider | Career questions, suggested bullets, and AI enhancement | +| Analysis tools | Free Check mode and public ATS checker; AI writing review with your own provider | Resume checker, summary generator, skills generator, and AI review | | Deployment | Hosted use or self-hosting | Hosted service | Resume-Now's free-builder guide says the no-cost download is a TXT file, and that downloading with a premium template requires paid access. Its FAQ says premium file formats require an upgrade. So free creation is not the same as a designed PDF or Word document. @@ -39,7 +39,7 @@ Both workflows still leave you to verify that every statement is accurate and th ## Reactive Resume limitations in this comparison -Reactive Resume has a smaller built-in writing-guidance surface. It has no equivalent to Resume-Now's prewritten suggestions, resume checker, summary generator, skills generator, or AI review. Its optional AI path also asks you to choose and configure a provider, which is more setup than a hosted suggestion workflow. +Reactive Resume has a smaller built-in writing-guidance surface. It has no equivalent to Resume-Now's prewritten suggestions, summary generator, or skills generator. Its Check mode focuses on how software reads the resume, and its AI writing review, like the rest of its AI path, asks you to choose and configure a provider, which is more setup than a hosted suggestion workflow. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-resumegemini.mdx b/docs/comparisons/reactive-resume-vs-resumegemini.mdx index c6d42bc84..ac127a77f 100644 --- a/docs/comparisons/reactive-resume-vs-resumegemini.mdx +++ b/docs/comparisons/reactive-resume-vs-resumegemini.mdx @@ -10,8 +10,8 @@ ResumeGemini offers prewritten examples and job-targeted AI suggestions while yo | Consideration | Reactive Resume | ResumeGemini | | --- | --- | --- | | Writing help | Direct editing; optional provider-configured AI | Prewritten expert content and job-specific examples | -| Job targeting | Manual edits or optional AI assistance | AI optimization, keyword alignment, and content tips for a target job | -| PDF | PDF export | Site documents free-plan PDF download and free/premium templates | +| Job targeting | Check mode's job match lists posting terms you have and lack; optional AI assistant | AI optimization, keyword alignment, and content tips for a target job | +| PDF | PDF export, plus Word (DOCX), Markdown, and JSON | Site documents free-plan PDF download and free/premium templates | ResumeGemini describes prewritten, job-specific examples, AI recommendations, keyword alignment, and content tips for a target job. Its product page documents free and premium templates plus a free-plan resume download. That covers at least one free template and free PDF path; it does not mean every template is free. @@ -23,15 +23,15 @@ Choose ResumeGemini when you want a content library and job-targeted AI optimiza ## Where Reactive Resume is a better fit -Use Reactive Resume to inspect or deploy the software yourself, keep resumes as structured data, or pick your own AI provider. It also exposes authenticated API and MCP workflows for automation. +Use Reactive Resume to inspect or deploy the software yourself, keep resumes as structured data, copy a resume for each job you track, or pick your own AI provider. It also exposes authenticated API and MCP workflows for automation. ## Which should you choose? -Choose ResumeGemini for built-in examples and keyword-oriented suggestions. Choose Reactive Resume for open-source access, self-hosting, and control over the AI provider and automation workflow. +Choose ResumeGemini for built-in examples and AI keyword optimization. Choose Reactive Resume for open-source access, self-hosting, and control over the AI provider and automation workflow. ## Reactive Resume limitations in this comparison -Reactive Resume has no built-in library of content examples and no native keyword-optimization product. Its AI workflow also needs provider credentials before you can use it. +Reactive Resume has no built-in library of content examples. Its job match shows which posting terms are missing but does not rewrite your resume for them on its own. Its AI assistant also needs provider credentials before you can use it. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-resumod.mdx b/docs/comparisons/reactive-resume-vs-resumod.mdx index 4a48c7dc9..9aefe1e4a 100644 --- a/docs/comparisons/reactive-resume-vs-resumod.mdx +++ b/docs/comparisons/reactive-resume-vs-resumod.mdx @@ -9,16 +9,16 @@ Resumod suits you when you want role-specific sample categories alongside its AT | Consideration | Reactive Resume | Resumod | | --- | --- | --- | -| Content help | Direct editing; optional provider-configured AI | Role-specific sample categories and a named AI Resume Builder | -| Checking tools | No equivalent built-in tool | Named ATS Resume Checker | +| Content help | Direct editing; optional AI assistant using your own provider | Role-specific sample categories and a named AI Resume Builder | +| Checking tools | Check mode in the editor and a free public ATS checker | Named ATS Resume Checker | Resumod's [product page](https://resumod.co/) lists role-specific sample categories and links to an [ATS Resume Checker](https://resumod.co/ats-resume-checker) and an [AI Resume Builder](https://resumod.co/ai-resume-builder). Those pages do not document how the tools work, what they output, or their full plan limits. -Reactive Resume exports PDF, DOCX, Markdown, and JSON. Its AI is optional: you connect and enable a provider with your own credentials, and that provider can cost extra. +Reactive Resume exports PDF, DOCX, Markdown, and JSON. Its [Check mode](/guides/checking-your-resume) and public [ATS checker](/guides/using-the-ats-checker) score how reliably software reads a resume. Its AI is optional: you connect and enable a provider with your own credentials, and that provider can cost extra. ## Where Resumod is a better fit -Choose Resumod when role-specific examples and its named ATS-checker and AI-builder tools are worth more to you than picking your own provider. +Choose Resumod when role-specific examples and its AI Resume Builder are worth more to you than picking your own provider. ## Where Reactive Resume is a better fit @@ -30,7 +30,7 @@ Choose Resumod for its bundled samples and named tools. Choose Reactive Resume f ## Reactive Resume limitations in this comparison -Reactive Resume has no built-in role-specific content library and no named ATS-checker tool. Its AI features also need provider setup before they work. +Reactive Resume has no built-in role-specific content library. Its AI features also need provider setup before they work. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-rezi.mdx b/docs/comparisons/reactive-resume-vs-rezi.mdx index a2ba9bc0e..8fa8886bb 100644 --- a/docs/comparisons/reactive-resume-vs-rezi.mdx +++ b/docs/comparisons/reactive-resume-vs-rezi.mdx @@ -9,8 +9,8 @@ Rezi suits you when you want its score, keyword targeting, AI writing, interview | Consideration | Reactive Resume | Rezi | | --- | --- | --- | -| AI | Optional; connect your own provider | Integrated AI writing and editing | -| Targeting | Manual edits or optional AI assistance | Rezi Score and keyword-targeting features | +| AI | Optional assistant; connect your own provider | Integrated AI writing and editing | +| Targeting | Check mode's job match lists posting terms you have and lack, without a score | Rezi Score and keyword-targeting features | | Review | No specialist human-review service | Expert resume-review option | | Interview help | No equivalent built-in interview tool | AI Interview feature | | Free limits | Core hosted workflow has no premium tier | One resume and three PDF downloads | @@ -18,7 +18,7 @@ Rezi suits you when you want its score, keyword targeting, AI writing, interview Rezi's pricing page documents the free plan's one-resume and three-PDF-download limits, plus integrated AI writing and editing. It also lists Rezi Score, keyword targeting, AI Interview, and an expert resume-review option. Those are documented product features, not a promise of ATS passage, interviews, or hiring results. -Reactive Resume's AI is optional and bring-your-own-provider: you configure the credentials, and provider use can cost extra. It has no equivalent to Rezi Score, keyword targeting, AI Interview, or expert review. +Reactive Resume's AI is optional and bring-your-own-provider: you configure the credentials, and provider use can cost extra. Its Check mode gives a readability score for how well software reads the resume and lists which posting terms are missing, but it has no equivalent to Rezi Score, AI Interview, or expert review. ## Where Rezi is a better fit @@ -34,7 +34,7 @@ Choose Rezi for a scoring and review-oriented workflow. Choose Reactive Resume f ## Reactive Resume limitations in this comparison -Reactive Resume has no equivalent to Rezi's score, keyword targeting, interview tool, or human review. Its AI features need provider setup and may cost extra through the provider. +Reactive Resume has no equivalent to Rezi's score, interview tool, or human review, and its job match does not score keyword coverage. Its AI features need provider setup and may cost extra through the provider. ## Sources diff --git a/docs/comparisons/reactive-resume-vs-zety.mdx b/docs/comparisons/reactive-resume-vs-zety.mdx index 4d09c018a..a318f1d88 100644 --- a/docs/comparisons/reactive-resume-vs-zety.mdx +++ b/docs/comparisons/reactive-resume-vs-zety.mdx @@ -3,16 +3,16 @@ title: "Reactive Resume vs Zety" description: "Compare Reactive Resume and Zety by free downloads, guided writing, resume checks, templates, open source, and self-hosting." --- -Zety is a guided resume builder with writing assistance, a cover-letter workflow, a resume check, and job-matching tools. Reactive Resume is an open-source editor with designed PDF and DOCX exports, and it has no equivalent prewritten guidance or job matching. +Zety is a guided resume builder with writing assistance, a cover-letter workflow, a resume check, and job-matching tools. Reactive Resume is an open-source editor with designed PDF and DOCX exports, and it has no equivalent prewritten guidance or job-listing matches. ## Quick comparison | Consideration | Reactive Resume | Zety | | --- | --- | --- | -| Free final download | PDF, DOCX, Markdown, and JSON | Plain-text TXT | +| Free final download | PDF, Word (DOCX), Markdown, and JSON | Plain-text TXT | | PDF and Word | Included exports | Paid formats | -| Guidance | Direct editing; optional provider-configured AI | Guided builder, templates, and writing resources | -| Related job tools | No equivalent job matching | Resume Check and Instant Job Matches listed in the free package | +| Guidance | Direct editing, Check mode, and an optional AI assistant using your own provider | Guided builder, templates, and writing resources | +| Related job tools | Cover letters and a job application tracker; no job-listing matches | Resume Check and Instant Job Matches listed in the free package | | Deployment | Hosted use or self-hosting | Hosted service | Zety's pricing page lists a TXT download in its free package. Its plan comparison names PDF, Word, and TXT among its download formats and shows TXT only for the free package. So the free package can write and export text, while a designed PDF or Word download sits in the paid workflow. @@ -27,7 +27,7 @@ That suits you if you want help choosing words, a matching cover letter, or a se ## Where Reactive Resume is a better fit -Reactive Resume fits when you want to download a designed PDF or DOCX without upgrading. Its templates render structured resume sections, and the [template guide](/guides/choosing-a-template) covers how to pick one. JSON export keeps your structured data for backup or reuse, separately from the final PDF or DOCX. +Reactive Resume fits when you want to download a designed PDF or DOCX without upgrading. Its templates render structured resume sections, and the [template guide](/guides/choosing-a-template) covers how to pick one. JSON export keeps your structured data for backup or reuse, separately from the final PDF or DOCX. Cover letters and a job application tracker are included at no cost, and Check mode flags parsing issues before you export. You also control where it runs. The source is public under the [MIT license](/legal/license), and the [Docker self-hosting guide](/self-hosting/docker) covers running your own instance. AI help is optional and uses a provider you configure. @@ -39,7 +39,7 @@ Either way, check the final wording and layout yourself. The tools help you prep ## Reactive Resume limitations in this comparison -Reactive Resume has no built-in library of prewritten writing guidance, no dedicated resume-check product, and no job matching. Its optional AI route needs provider setup, and self-hosting means running a server. Zety is more convenient if you want those hosted services next to the builder and do not need a free PDF or Word export. +Reactive Resume has no built-in library of prewritten writing guidance and no job-listing matches; its tracker records jobs you find elsewhere. Its optional AI route needs provider setup, and self-hosting means running a server. Zety is more convenient if you want those hosted services next to the builder and do not need a free PDF or Word export. ## Sources diff --git a/docs/contributing/architecture.mdx b/docs/contributing/architecture.mdx index 2ff06ccef..6adf748df 100644 --- a/docs/contributing/architecture.mdx +++ b/docs/contributing/architecture.mdx @@ -1,143 +1,147 @@ --- title: "Project architecture" -description: "How the Reactive Resume monorepo is laid out, the runtime boundaries between the web and server apps, and the package ownership model." +description: "How the Reactive Resume monorepo fits together: the web and server apps, shared packages, runtime boundaries, and where new code belongs." --- -Reactive Resume is a pnpm/Turborepo monorepo. Docker runs one Node.js process. Vercel deploys two services from one project: `frontend` serves static assets through its CDN, and `backend` runs the same Hono application in a Node.js Function. Both targets share the web app, API, authentication, renderers, and database schema. +Reactive Resume is a TypeScript monorepo managed with pnpm workspaces and Turborepo. This page explains how the pieces fit together, so you can find the code behind a feature and know where a change belongs. To get a working checkout first, see [Development setup](/contributing/development). -Internal packages are source-consumed through their `package.json` export maps. Import package subpaths, not another workspace's private `src` files. +## The big picture ---- +There are two apps and a set of shared packages: -## Runtime shape +- **`apps/web`** is a client-rendered React 19 single-page app built with Vite, TanStack Router, TanStack Query, Tailwind CSS, and Lingui for translations. +- **`apps/server`** is a Hono application on Node.js. It serves the API, authentication, the MCP server, uploads, OpenAPI, and the built web app. +- **`packages/*`** hold everything the apps share: API business logic, authentication, database access, schemas, PDF and DOCX rendering, and UI primitives. + +The browser talks to the server through [oRPC](https://orpc.unnoq.com/) at `/api/rpc`. [Better Auth](https://www.better-auth.com/) handles sign-in, sessions, passkeys, two-factor authentication, API keys, and the OAuth provider used by MCP clients. [Drizzle](https://orm.drizzle.team/) talks to PostgreSQL. ```mermaid flowchart TD - Browser["Browser"] --> WebRoutes["apps/web routes"] - WebRoutes --> ORPCClient["oRPC client"] - ORPCClient --> RPC["/api/rpc"] + Browser["Browser: apps/web SPA"] -->|"oRPC /api/rpc"| Server + Browser -->|"Forme (WebAssembly)"| BrowserPDF["PDF in the browser"] + MCPClient["MCP client"] -->|"/mcp"| Server - subgraph NodeProcess["Node process"] - Server["apps/server Hono adapter"] - API["packages/api feature routers"] - Auth["packages/auth"] + subgraph Server["apps/server (Hono)"] + RPC["RPC and OpenAPI handlers"] + AuthRoutes["/api/auth"] MCP["packages/mcp"] - PDFServer["@reactive-resume/pdf/server"] end - Server --> RPC - RPC --> API - Server --> Auth - Server --> MCP - API --> PDFServer - API --> DB["packages/db"] - API --> Storage["Local disk, S3, or private Vercel Blob"] - DB --> Postgres["PostgreSQL"] + RPC --> API["packages/api feature routers"] + MCP --> API + AuthRoutes --> Auth["packages/auth (Better Auth)"] + API --> DB["packages/db (Drizzle)"] --> Postgres[("PostgreSQL")] + API --> Storage[("Local disk, S3, or Vercel Blob")] + API --> Redis[("Redis (optional)")] + API --> ServerPDF["@reactive-resume/pdf/server"] ``` -`apps/web` owns the React SPA with TanStack Router and Vite. `apps/server` owns the Hono application and mounts RPC, auth, OpenAPI, MCP, static uploads, schema JSON, and the built web app. +### How it runs ---- +- **Development.** `pnpm dev` starts Vite on `PORT` (default `3000`), the Hono server on `SERVER_PORT` (default `3001`), and the email template preview on port `3002`. Vite proxies `/api`, `/mcp`, `/uploads`, `/.well-known`, and `/schema.json` to Hono, so you always open `http://localhost:3000`. +- **Docker.** The production image runs one Node.js process on port `3000`. Hono mounts the API, auth, MCP, and static routes, then serves the built web app. +- **Vercel.** One project deploys two services: `frontend` serves the static web build from the CDN, and `backend` runs the same Hono app in a Node.js Function. See [Deployment checks](/contributing/deployment-checks). + +There is no request-time React server rendering. The web build prerenders the marketing homepage for each locale, and `apps/server/src/static/web.ts` serves HTML shells with OpenGraph, canonical, and JSON-LD metadata injected. + +### What happens at startup + +The server checks the environment, applies database migrations, and verifies the migrated schema before it initializes auth and accepts traffic. With `STRICT_SCHEMA_CHECK=true`, schema drift stops the server; otherwise it logs the drift and continues. ## Workspace map -| Workspace | Ownership | +| Workspace | What it owns | | --- | --- | -| `apps/web` | TanStack Router routes, web features, browser PDF.js preview/viewer code, PWA setup, oRPC browser client | -| `apps/server` | Hono route composition, production HTTP adapters, MCP transport, OpenAPI/well-known handlers, static file serving, startup checks | -| `packages/api` | oRPC procedures and feature-owned business behavior under `src/features/*` | -| `packages/auth` | Better Auth config, auth helpers, and exported auth types | -| `packages/db` | Drizzle client and schema; root `migrations/` stores generated migrations | -| `packages/env` | Server environment validation and root `.env` loading | -| `packages/schema` | Zod schemas and typed resume/page/template models | -| `packages/resume` | Pure resume-domain helpers, including JSON Patch behavior and network icon mapping | -| `packages/pdf` | Resume document, template primitives, Forme conversion, templates, font registration, and browser/server generation adapters | -| `packages/docx` | DOCX export generation | -| `packages/mcp` | MCP tools, prompts, resources, server card, and tool metadata | -| `packages/ui` | Shared Base UI/shadcn-style primitives and hooks | -| `packages/ai` | AI provider types, prompts, resume parsing/sanitization helpers, and model-facing tool contracts | +| `apps/web` | Routes (`src/routes`, file-based), user-facing features (`src/features`), the PDF.js preview and public viewer, the PWA, and the oRPC browser client | +| `apps/server` | Hono route composition (`src/http`), RPC and OpenAPI adapters, the MCP transport, static and upload handlers, SEO for HTML shells, and startup checks | +| `packages/api` | oRPC procedures and business logic, one folder per feature under `src/features/*` | +| `packages/auth` | Better Auth configuration, helpers, and types | +| `packages/db` | Drizzle client and schema; generated migrations live in the root `migrations/` folder | +| `packages/env` | Server environment validation; loads the root `.env` | +| `packages/schema` | Zod schemas for resumes, cover letters, applications, pages, and templates | +| `packages/resume` | Pure resume logic with no database, HTTP, or DOM dependencies, such as JSON Patch helpers and social network icons | +| `packages/pdf` | Resume templates (which also lay out cover letters), the Forme adapter, font resolution, custom-style support, and browser and server PDF adapters | +| `packages/docx` | DOCX export | +| `packages/mcp` | MCP tools, prompts, resources, and the server card | +| `packages/ai` | AI provider types, prompts, and model-facing helpers | | `packages/import` | Resume importers | +| `packages/ui` | Shared UI primitives and hooks in the Base UI / shadcn style | | `packages/fonts` | Font metadata | | `packages/email` | Email transport and templates | -| `packages/utils` | Narrow cross-cutting utilities with explicit export subpaths | -| `packages/config` | Shared development configuration | -| `tooling` | Development-only scripts and repo tooling | +| `packages/utils` | Narrow cross-cutting helpers behind explicit export subpaths | +| `packages/config` | Shared TypeScript and tooling configuration | +| `packages/dsh-plugin` | A separately built and published plugin that connects a DeepSeek Harness session to Reactive Resume over MCP | +| `tooling` | Development-only scripts: PDF translation catalog, semantic CSS reference, icon builds, database reset, deployment smoke test | ---- +Internal packages are consumed as source through the `exports` map in each `package.json`, which points at `src` files. Don't expect a `dist` folder unless a package builds one explicitly. + +## Where new code goes + +| You are changing | Put it here | +| --- | --- | +| A page, loader, or user workflow | A route in `apps/web/src/routes` plus the feature folder in `apps/web/src/features/` | +| An authenticated API procedure or business rule | `packages/api/src/features/` | +| Pure resume data behavior | `packages/resume` | +| The shape of resume data | `packages/schema` first, then API DTOs, importers, PDF templates, and web forms that use it | +| A PDF template or rendering behavior | `packages/pdf` | +| PDF.js canvas or viewer UI | `apps/web/src/features/resume` (never `packages/pdf`) | +| DOCX export | `packages/docx` | +| An MCP tool, prompt, or resource | `packages/mcp` | +| A generic UI primitive or hook | `packages/ui`; workflow-specific UI stays in its web feature | +| A database column or table | `packages/db/src/schema/*`, then `pnpm db:generate` | +| A server environment variable | `packages/env/src/server.ts`, `.env.example`, and `globalEnv` in `turbo.json` | +| A dev-only script | `tooling/` | + +A new template touches several places: `packages/schema/src/templates.ts`, `packages/pdf/src/templates/index.ts`, the template source under `packages/pdf/src/templates//`, and preview images under `apps/web/public/templates/{jpg,pdf}`. + +Add a helper to `packages/utils` only when no domain package is a better owner. JSON Patch behavior belongs in `@reactive-resume/resume/patch`, and DOCX builders belong in `@reactive-resume/docx`. ## Boundary rules -- Use `@reactive-resume/*` package exports for cross-workspace imports. -- Do not import another workspace through `apps/**`, `packages/**`, `@reactive-resume/*/src/**`, or a TypeScript path alias to another workspace's `src`. -- Keep browser-only code in web features or explicit browser subpaths. -- Keep server-only code in server packages or explicit server subpaths. -- Keep environment-neutral domain packages free of DB, HTTP, DOM, and app imports. -- Add public package exports deliberately. Wildcard exports are reserved for leaf-style public surfaces such as UI components/hooks and schema resume files. +Turborepo enforces these rules with `pnpm exec turbo boundaries`: -The checks are executable: +- Import other workspaces by package name and export subpath, such as `@reactive-resume/pdf/browser`. Never reach into another workspace's `src` through a relative path, `@reactive-resume/*/src/*`, or a TypeScript path alias. +- Each workspace's `turbo.json` declares tags. `app:web` and `app:server` mark the apps. `runtime:server` marks server-only packages (API, auth, database, environment, email, MCP), `runtime:browser` marks browser-only UI, and `runtime:universal` marks environment-neutral domain packages. +- Runtime-specific code sits behind explicit subpaths such as `@reactive-resume/pdf/browser`, `@reactive-resume/pdf/server`, and `@reactive-resume/env/server`. Keep root exports environment-neutral unless the whole package is server-only. +- Wildcard exports are reserved for leaf libraries with a file-like surface: `@reactive-resume/ui/components/*`, `@reactive-resume/ui/hooks/*`, and the schema model files. Everything else uses explicit exports. -```bash -pnpm exec turbo boundaries -pnpm exec biome check biome.json turbo.json tooling/grit/no-cross-workspace-src-imports.grit apps/web/tsconfig.json apps/*/turbo.json packages/*/turbo.json -``` +After you change a shared contract, an export, or an import path, run `pnpm exec turbo boundaries` and check the affected consumers. ---- +## The web app -## Feature placement +`apps/web/src/routes` stays route-owned: route files handle the URL, loaders, redirects, and metadata. Implementation lives in `apps/web/src/features`, grouped by product area: `documents`, `resume` (editor, preview, export, sharing, custom styles), `letters`, `applications`, `assistant`, `ats-checker`, `settings`, `command-palette`, `auth`, `homepage`, `theme`, `locale`, and `user`. -When adding code, choose the owner by behavior: +`apps/web/src/router.tsx` creates the router context with `queryClient`, `orpc`, `theme`, `locale`, `session`, and `flags`. Read these from route context instead of fetching them again. Never edit `routeTree.gen.ts` by hand; Vite regenerates it when you add or rename a route. -| Change | Put it here | -| --- | --- | -| Route, loader, route-level server handler, or web workflow | `apps/web/src/routes` plus `apps/web/src/features/` | -| API procedure or authenticated business behavior | `packages/api/src/features/` | -| Pure resume data logic | `packages/resume` | -| Resume schema or template list shape | `packages/schema` | -| PDF template/rendering behavior | `packages/pdf` | -| PDF.js canvas/viewer UI | `apps/web/src/features/resume` | -| DOCX export behavior | `packages/docx` | -| MCP tool/prompt/resource behavior | `packages/mcp` | -| Shared UI primitive/hook | `packages/ui` | -| Cross-cutting helper | Prefer a domain package first; otherwise add an explicit `packages/utils` export | +The oRPC client in `apps/web/src/libs/orpc/client.ts` calls `/api/rpc` with credentials. On Vercel, `apps/web/src/libs/orpc/fetch.ts` stages large request bodies through Blob storage. ---- +When you add a public marketing route, also update its server fallback and SEO handling in `apps/server/src/static/web.ts`. Vite's dev fallback can hide a production 404. -## Web layout +## The API -`apps/web/src/routes` stays route-owned. Route files handle URL shape, loaders, redirects, metadata, and SSR flags. +`packages/api/src/routers/index.ts` combines the feature routers (`resume`, `coverLetters`, `documents`, `applications`, `agent`, `ai`, `aiProviders`, `auth`, `storage`, `statistics`, `flags`) into the contract served at `/api/rpc`. Each feature folder owns its procedures, services, helpers, and tests. -Domain UI and browser-heavy implementation code lives under `apps/web/src/features`. Current feature areas include resume preview/export/public pages, command palette, auth, settings, theme, locale, and user menu behavior. +Use `protectedProcedure` from `packages/api/src/context.ts` for authenticated procedures, and check resource ownership inside the feature logic. API keys, bearer tokens, and cookies all resolve through the same shared auth path; don't add a separate one. -Generic app-local components remain in `apps/web/src/components`; shared reusable primitives live in `packages/ui`. +Keep helpers inside the feature that uses them. Don't reintroduce technical-layer folders such as `services/` or `helpers/` at the package root. -Dialog runtime state is centralized in `apps/web/src/dialogs/store.ts`, while dialog schemas and renderers are registered by domain under `apps/web/src/dialogs/{auth,api-key,resume}`. +## PDF rendering ---- +`packages/pdf` renders every PDF. Templates are React components built from the package's primitives. The code in `src/forme` renders them with a small React reconciler and converts the result into a [Forme](https://www.formepdf.com/) document. The Forme engine, compiled to WebAssembly, lays out and draws the pages. No Chromium, Browserless, or print service is involved. -## API layout +- `@reactive-resume/pdf/browser` creates PDFs in the browser. The editor's download and preview use it. +- `@reactive-resume/pdf/server` creates PDFs on the server, for the public resume download and API exports. +- `packages/pdf/src/templates/shared/filtering.ts` holds the section filtering shared by all templates. Template-specific visual exceptions stay in that template's folder. +- `packages/pdf/src/hooks/use-register-fonts.ts` resolves font families, weights, and fallback stacks for other scripts. -`packages/api/src/routers/index.ts` exports the top-level oRPC contract. Feature modules under `packages/api/src/features/*` own their procedure modules, services, helpers, tests, and public package exports. +Default section titles in the PDF come from a generated catalog, `packages/pdf/src/section-title-catalog.json`, built from the web app's translations. See [Contributing translations](/contributing/translations#updating-catalogs-in-a-checkout). -Avoid reintroducing technical-layer folders such as `services/` or `helpers/` at the package root. If a helper is used by one feature, keep it in that feature. If it becomes shared, name the shared capability explicitly and export it intentionally. +## MCP ---- +`packages/mcp` implements the MCP server with canonical, unprefixed tool names such as `list_resumes`, `read_resume`, `apply_resume_patch`, `list_cover_letters`, and `list_applications`. The server process imports it from `@reactive-resume/mcp` and injects an in-process oRPC router client, so MCP tools run the same business logic as the web app. MCP must never import code from `apps/web`. For the user-facing side, see [Using the MCP server](/guides/using-the-mcp-server). -## PDF and export boundaries +## Related pages -`packages/pdf` owns PDF generation. Templates are written with React primitives; `src/forme` renders them with a small React renderer and converts the result into a [Forme](https://www.formepdf.com/) document, which the Forme engine (WebAssembly) lays out and draws: - -- `@reactive-resume/pdf/browser` creates browser PDF blobs. -- `@reactive-resume/pdf/server` creates server PDF files. -- Template code stays under `packages/pdf/src/templates`. - -Localized section-title resolution stays in the caller because it depends on web/server locale context. PDF.js preview and viewer code stays in `apps/web/src/features/resume`, not in `packages/pdf`. - -DOCX export generation lives in `packages/docx`. - ---- - -## MCP boundary - -MCP implementation lives in `packages/mcp`. It exposes canonical unprefixed tool names such as `list_resumes`, `read_resume`, and `apply_resume_patch`. - -The server process imports MCP from `@reactive-resume/mcp` and injects the in-process oRPC router client. It must not import MCP code from `apps/web/src`. +- [Development setup](/contributing/development): run the app locally and learn the everyday commands. +- [Deployment checks](/contributing/deployment-checks): how CI verifies the Vercel build and how to smoke-test an installation. +- [Contributing translations](/contributing/translations): Crowdin, the glossary, and catalog commands. diff --git a/docs/contributing/deployment-checks.mdx b/docs/contributing/deployment-checks.mdx index 8b937c404..af0edb833 100644 --- a/docs/contributing/deployment-checks.mdx +++ b/docs/contributing/deployment-checks.mdx @@ -1,51 +1,70 @@ --- title: "Deployment checks" -description: "How CI verifies the Vercel build artifact, and how to run the deployment smoke test against Vercel or Docker." +description: "How CI verifies the Vercel build artifact on every pull request, and how to run the deployment smoke test against a Vercel or Docker installation." --- -The **Vercel compatibility** workflow (`.github/workflows/vercel.yml`) has two jobs. +Reactive Resume ships as a Docker image and as a Vercel project. The **Vercel compatibility** workflow (`.github/workflows/vercel.yml`) catches problems that only show up in a deployed build. It has two jobs: an offline artifact build that runs on every pull request, and a live smoke test you start by hand. You can run the same smoke test against your own installation. ## Artifact build -Runs on every pull request and every push to `main`. It needs no Vercel account and no secrets, so fork pull requests run it safely. +The `artifact` job runs on every pull request, every push to `main`, and every manual run. It needs no Vercel account and no secrets, so pull requests from forks run it safely. The job: 1. Starts an isolated PostgreSQL service. -2. Writes a local `.vercel/project.json` with the `services` framework and runs `vercel build --prod` offline, with placeholder Blob credentials. This also applies migrations to the isolated database. +2. Writes a local `.vercel/project.json` with the `services` framework, then runs `vercel build --prod` offline with placeholder Blob credentials. The build applies migrations to the isolated database. 3. Checks the `backend` service Function: - - runtime is `nodejs24.x`, `maxDuration` is `300`, and the handler is `apps/server/vercel.mjs`; - - a copy of the Function outside the checkout loads with `--no-experimental-require-module`, which matches the Vercel runtime, so a dependency the build left out fails the job; - - the copy rejects an unauthenticated staging request and serves the prerendered homepage. + - its runtime is `nodejs24.x`, `maxDuration` is `300`, and its handler is `apps/server/vercel.mjs`; + - a copy of the Function outside the checkout loads with `--no-experimental-require-module`, like the Vercel runtime does, so a dependency the build left out fails the job; + - the copy rejects an unauthenticated upload-staging request with `401` and serves the prerendered homepage with its JSON-LD metadata. -If a new server dependency fails the loading check, add it and its own dependencies to `bundledInteropPackages` in `apps/server/tsdown.config.ts`. CommonJS dependencies need this: Vercel's service builder loads them through pnpm links that it leaves out of the Function. +### When the loading check fails + +If a new server dependency breaks the loading check, add it and its own dependencies to `bundledInteropPackages` in `apps/server/tsdown.config.ts`. CommonJS dependencies need this, because Vercel's service builder leaves out the pnpm links they load through. ## Live smoke test -Runs only when started manually (`workflow_dispatch`). It uses the `vercel-smoke` GitHub environment and runs `tooling/deployment/smoke.mjs` against `VERCEL_SMOKE_URL`. +The `live-smoke` job runs only when you start the workflow manually (`workflow_dispatch`). It uses the `vercel-smoke` GitHub environment and runs `tooling/deployment/smoke.mjs` against the installation at `VERCEL_SMOKE_URL`. - Point the smoke test only at a dedicated test installation. It creates an account, a public resume, and files, then deletes them. + Point the smoke test only at a dedicated test installation. It signs up a new account, publishes a resume, and uploads files, then deletes the account and everything in it. -The script checks health, public pages, signup, resume CRUD, public PDF rendering, a 10 MiB upload and download, and one-time use of staged requests. +The script checks, in order: -To also check a 25 MiB agent attachment, configure a deterministic OpenAI-compatible test provider that serves the model `smoke-model`: +1. `/api/health` reports `healthy`, and `/`, `/auth/login`, `/robots.txt`, `/sitemap.xml`, and `/.well-known/oauth-protected-resource` respond. A missing asset returns `404`. +2. An email sign-up creates a session. +3. A resume created from sample data can be read back and made public, and its public page and server-rendered public PDF load. +4. A 10 MiB file uploads, downloads intact, and is deleted. On Vercel the upload goes through a staged request, and the script checks that a staged request can't be replayed. +5. Optionally, a 25 MiB assistant attachment uploads and is deleted (see below). +6. The account is deleted, even if an earlier check failed. + +The installation must allow sign-ups and email sign-in, so `FLAG_DISABLE_SIGNUPS` and `FLAG_DISABLE_EMAIL_AUTH` must not be `true`. + +### Attachment check + +To include the 25 MiB attachment check, configure an OpenAI-compatible test provider that answers a connection test for the model `smoke-model`. No paid AI model is needed; a deterministic stub works. | Name | Kind | Value | | --- | --- | --- | -| `VERCEL_SMOKE_URL` | Variable | Test installation origin | -| `VERCEL_SMOKE_AI_BASE_URL` | Variable | Test provider base URL | -| `VERCEL_SMOKE_AI_API_KEY` | Secret | Test provider API key | +| `VERCEL_SMOKE_URL` | Variable | Origin of the test installation | +| `VERCEL_SMOKE_AI_BASE_URL` | Variable | Base URL of the test provider | +| `VERCEL_SMOKE_AI_API_KEY` | Secret | API key for the test provider | -No paid AI model is needed. +The installation needs `ENCRYPTION_SECRET` set to save the provider. A provider on a private or `http://` address also needs `FLAG_ALLOW_UNSAFE_AI_BASE_URL=true`, which is only safe on an isolated test installation. -## Run the smoke test locally +## Run the smoke test yourself -Against a local Docker installation: +The script needs only Node.js 24 and a checkout. Set `SMOKE_URL` to the installation's origin: ```bash SMOKE_URL=http://localhost:3000 node tooling/deployment/smoke.mjs ``` -Add `SMOKE_AI_BASE_URL` and `SMOKE_AI_API_KEY` to include the attachment check. +Add `SMOKE_AI_BASE_URL` and `SMOKE_AI_API_KEY` to include the attachment check. The script detects the platform on its own: on Docker, the staging endpoint returns `404` and uploads go directly to the server. + +## Related pages + +- [Development setup](/contributing/development): run the app and the test suites locally. +- [Deploying to Vercel](/self-hosting/vercel): set up your own Vercel installation. +- [Self-hosting with Docker](/self-hosting/docker): run the production image. diff --git a/docs/contributing/development.mdx b/docs/contributing/development.mdx index afebd7512..0cb80bc25 100644 --- a/docs/contributing/development.mdx +++ b/docs/contributing/development.mdx @@ -1,324 +1,297 @@ --- title: "Development setup" -description: "Set up a local development environment for Reactive Resume with pnpm, Docker services, environment variables, and the web and server apps." +description: "Run Reactive Resume locally with Node.js 24, pnpm, and Docker, then use the everyday commands for the database, tests, linting, and pull requests." --- - - **Prerequisites**: - [Node.js](https://nodejs.org/) v24 - [pnpm](https://pnpm.io/) v11.21.0 - - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/) - +This guide takes you from a fresh clone to a running local copy of Reactive Resume, then covers the commands you'll use while working on it. For how the code is organized, read [Project architecture](/contributing/architecture). -These steps set up Reactive Resume for local development, whether you're contributing to the project or customizing it for yourself. +## Before you start ---- +You need: -## Setting up your development environment +- **[Node.js](https://nodejs.org/) 24.** The version is pinned in `.nvmrc` and the root `engines` field, so `nvm use` or `fnm use` picks it up. +- **[pnpm](https://pnpm.io/installation) 12.** The root `packageManager` field pins the exact version (currently `pnpm@12.8.1`), and pnpm switches to it automatically when you run it inside the repository. +- **[Docker](https://docs.docker.com/get-docker/) with Docker Compose** for PostgreSQL, Redis, and S3-compatible storage. Start the Docker daemon first. +- **[Git](https://git-scm.com/).** + +## Set up your checkout + +Run every command from the repository root unless a step says otherwise. - - ```bash - git clone https://github.com/reactive-resume/reactive-resume.git reactive-resume - cd reactive-resume - ``` - - - - Install [pnpm](https://pnpm.io/installation) directly, then install the project dependencies: + + ```bash + git clone https://github.com/reactive-resume/reactive-resume.git + cd reactive-resume + ``` + - ```bash - pnpm install - ``` - - - - If you want to run the app directly on your machine with `pnpm dev`, start only the infrastructure services: - - ```bash - docker compose -f compose.dev.yml up -d postgres redis seaweedfs seaweedfs_create_bucket - ``` - - This starts the following infrastructure services: - - **PostgreSQL** — Database (port 5432) - - **Redis** — AI Agent workspace streams/state (port 6379) - - **SeaweedFS** — S3-compatible storage (port 8333) + + ```bash + pnpm install --frozen-lockfile + ``` - - **From v5.1.0 onwards** — PDF generation now runs entirely in the browser with the Forme PDF engine (WebAssembly), so no Browserless or Chromium container is required for development. - - - - `compose.dev.yml` can also run the app in a development container with `docker compose -f compose.dev.yml up -d`. - Use the service-filtered command above when you want local editor tooling and `pnpm dev` on the host. - - - - Wait for all services to be healthy before proceeding. Check with `docker compose -f compose.dev.yml ps`. - - - - - Copy `.env.example` to `.env.local` in the project root: + The install also sets up the [Lefthook](https://github.com/evilmartians/lefthook) Git hooks described in [Commits and pull requests](#commits-and-pull-requests). + - ```bash - cp .env.example .env.local - ``` + + ```bash + docker compose -f compose.dev.yml up -d postgres redis seaweedfs seaweedfs_create_bucket + docker compose -f compose.dev.yml ps + ``` - Then edit `.env.local` as needed. For local development on the host, set at minimum: + This starts: - ```bash - # Application - PORT=3000 - SERVER_PORT=3001 - APP_URL=http://localhost:3000 + | Service | Purpose | Port | + | --- | --- | --- | + | `postgres` | The database | `5432` | + | `redis` | Optional: shared rate limits, resumable assistant replies, live resume events, and view de-duplication | `6379` | + | `seaweedfs` | S3-compatible storage for uploads | `8333` | + | `seaweedfs_create_bucket` | Creates the `reactive-resume` bucket, then exits | — | - # Database - DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres + Wait until `ps` shows the services as healthy. PDF generation runs on the Forme engine in WebAssembly, so you don't need a Chromium or Browserless container. + - # Authentication - AUTH_SECRET=development-secret-change-in-production + + Copy the template only if you don't have a `.env.local` yet: - # Storage (SeaweedFS) - S3_ACCESS_KEY_ID=seaweedfs - S3_SECRET_ACCESS_KEY=seaweedfs - S3_ENDPOINT=http://localhost:8333 - S3_BUCKET=reactive-resume - S3_FORCE_PATH_STYLE=true + ```bash + test -e .env.local || cp .env.example .env.local + ``` - # Email (Mailpit for local development) - SMTP_HOST=localhost - SMTP_PORT=1025 - SMTP_FROM="Reactive Resume " + The template uses container hostnames. Because the app runs on your machine, change these values in `.env.local` to `localhost`: - # AI Agent workspace and saved AI providers - REDIS_URL=redis://localhost:6379 - ENCRYPTION_SECRET=change-me-to-a-secure-agent-secret-in-production - ``` + ```dotenv + APP_URL=http://localhost:3000 + DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres + S3_ENDPOINT=http://localhost:8333 + REDIS_URL=redis://localhost:6379 + ``` - - **Email testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/). Emails the app sends are captured there and viewable at [http://localhost:8025](http://localhost:8025), so nothing reaches a real address during development. - + Then generate the secrets: - - - - The server startup path runs migrations before serving traffic. To apply migrations manually without starting the app, - run the root migration script, which loads `.env.local` before invoking Drizzle Kit: - - ```bash - pnpm run db:migrate - ``` - - - - ```bash - pnpm run dev - ``` - - Your local Reactive Resume instance will be available at [http://localhost:3000](http://localhost:3000). - + ```bash + openssl rand -hex 32 # paste the output into AUTH_SECRET + openssl rand -hex 32 # paste the output into ENCRYPTION_SECRET + ``` + + `AUTH_SECRET` is required. `ENCRYPTION_SECRET` (at least 32 characters) is needed only for saved AI providers and the assistant, but it's easiest to set it now. Redis is optional; with it, assistant replies survive a page reload and rate limits are shared between server processes. Every variable is described in [Environment variables](/self-hosting/environment-variables). + + + Working on something that doesn't need uploads? Start only `postgres` and set `STORAGE_BACKEND=local`. Files then go to the `data/` folder in your checkout. + + + + + ```bash + pnpm dev + ``` + + Open [http://localhost:3000](http://localhost:3000). The server applies database migrations on startup, so a fresh database is ready as soon as the app loads. + + + + Select **Sign up** and create an account. Without SMTP settings, the app doesn't send email: verification and password-reset links are printed in the terminal running `pnpm dev`. Copy the link from there into your browser. + ---- +### What `pnpm dev` runs -## Available scripts +`pnpm dev` loads `.env.local` through dotenvx and starts three processes with Turborepo: -The scripts you will use most during development: +| Process | Address | Notes | +| --- | --- | --- | +| Vite (web app) | `http://localhost:3000` (`PORT`) | Hot reload. Proxies `/api`, `/mcp`, `/uploads`, `/.well-known`, and `/schema.json` to the server. | +| Hono (server) | `http://localhost:3001` (`SERVER_PORT`) | Restarts on change through `tsx watch`. | +| Email preview | `http://localhost:3002` | Previews the email templates in `packages/email`. | -### Development +Use `pnpm dev:web` to start only Vite. API calls still need a server running. -| Command | Description | -| ------------------------------ | ----------------------------------------------------------- | -| `pnpm dev` | Start the web and server development processes | -| `pnpm build` | Build the production web bundle and server bundle | -| `pnpm start` | Start the built production server | -| `pnpm typecheck` | Run TypeScript type checking | -| `pnpm test` | Run Vitest across workspaces | -| `pnpm exec biome check .` | Run a non-mutating Biome check | -| `pnpm check` | Run Biome with write/fix behavior (`--write --unsafe`) | -| `pnpm exec turbo boundaries` | Check workspace/package boundary rules | +## Everyday commands -### Database +| Command | What it does | +| --- | --- | +| `pnpm dev` | Start the web app, server, and email preview | +| `pnpm dev:web` | Start only the web app | +| `pnpm build` | Build the web app and server for production (regenerates PDF translations first) | +| `NODE_ENV=production pnpm start` | Run the built server on `PORT`. It reads exported variables or the root `.env`, not `.env.local`. | +| `pnpm typecheck` | Type-check every workspace with `tsgo` | +| `pnpm test` | Run the Vitest suites of every workspace | +| `pnpm test:e2e` | Run the Playwright browser tests (see [Browser tests](#browser-tests)) | +| `pnpm exec biome check ` | Lint and format-check without changing files | +| `pnpm check` | **Changes files:** regenerates PDF translations and runs Biome with `--write --unsafe` | +| `pnpm exec turbo boundaries` | Check package boundary rules | +| `pnpm knip` | Find unused files, exports, and dependencies | +| `pnpm lingui:extract` | Extract new UI strings into `apps/web/locales/*.po` | +| `pnpm docs:gen` | Regenerate the OpenAPI spec and the custom-styles CSS reference | -| Command | Description | -| ---------------------- | -------------------------------------------- | -| `pnpm db:generate` | Generate migration files from schema changes | -| `pnpm db:migrate` | Apply pending migrations | -| `pnpm db:studio` | Open Drizzle Studio (database GUI) | - -### Internationalization - -| Command | Description | -| ------------------------- | -------------------------------------- | -| `pnpm run lingui:extract` | Extract translatable strings from code | - -## Understanding the project structure - -``` -reactive-resume/ -├── apps/ -│ ├── web/ # TanStack Router routes, web features, and browser UI -│ └── server/ # Hono production server, HTTP adapters, static serving -├── packages/ -│ ├── api/ # oRPC features and business behavior -│ ├── auth/ # Better Auth configuration and helpers -│ ├── db/ # Drizzle client and schema -│ ├── docx/ # DOCX export generation -│ ├── mcp/ # MCP tools, prompts, resources, and metadata -│ ├── pdf/ # PDF rendering (Forme) and PDF generation adapters -│ ├── resume/ # Pure resume-domain helpers -│ ├── schema/ # Zod schemas and typed models -│ ├── ui/ # Shared Base UI/shadcn-style primitives -│ └── ... -├── tooling/ # Development-only scripts and repository tooling -├── migrations/ # Generated database migrations -├── docs/ # Documentation -└── data/ # Local development data and uploads -``` - ---- - -## Working with the database - -### Viewing the database - -Use Drizzle Studio to explore and manage your database: +Prefer package-scoped commands while you work. Package names come from each `package.json`: the apps are `web` and `server`, and shared packages are `@reactive-resume/`. ```bash -pnpm run db:studio +pnpm --filter web typecheck +pnpm --filter @reactive-resume/pdf test +pnpm exec biome check apps/web/src/features/resume ``` -This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizzle.studio). +## Work with the database -### Making schema changes +| Command | What it does | +| --- | --- | +| `pnpm db:generate` | Generate a migration from schema changes | +| `pnpm db:migrate` | Apply pending migrations without starting the app | +| `pnpm db:studio` | Open Drizzle Studio at [local.drizzle.studio](https://local.drizzle.studio) | -1. Edit the schema in `packages/db/src/schema/*` -2. Generate a migration: - ```bash - pnpm run db:generate - ``` -3. Apply the migration: - ```bash - pnpm run db:migrate - ``` +All three load `.env.local` before calling Drizzle Kit, which doesn't read `.env` files by itself. -Always review generated migrations before applying them, especially when working with existing data. +To change the schema: ---- +1. Edit the tables in `packages/db/src/schema/*`. +2. Run `pnpm db:generate`. The migration is written to the root `migrations/` folder. +3. Read the generated SQL, then apply it with `pnpm db:migrate` or by restarting `pnpm dev`. -## Working with translations + + Review every generated migration before you apply it. Don't reset the database or delete Docker volumes to work around a setup error; find the cause instead. + -Reactive Resume uses [Lingui](https://lingui.dev/) for internationalization. +## Run tests -### Adding translatable text +### Unit and integration tests -Use the `t` macro for strings or `` component for JSX: +Tests use [Vitest](https://vitest.dev/) and sit next to the code they cover as `*.test.ts(x)` or `*.spec.ts(x)`. Most packages run in Node; `packages/ui` uses `happy-dom`. + +```bash +# One package +pnpm --filter @reactive-resume/pdf test + +# One file (the path is relative to the package) +pnpm --filter @reactive-resume/pdf test src/templates/shared/filtering.test.ts + +# One test by name +pnpm --filter @reactive-resume/pdf exec vitest run src/templates/shared/filtering.test.ts -t "filterItems" + +# Coverage (V8, written to the package's coverage/ folder) +pnpm --filter @reactive-resume/pdf test:coverage +``` + +Pass file paths straight after `test`. An extra `--` stops Vitest from filtering the run. Most test scripts use `--passWithNoTests`, so a green run with zero tests proves nothing about your change. + +Two suites need a real PostgreSQL database: set `COVER_LETTER_TEST_DATABASE_URL` and `OAUTH_TEST_DATABASE_URL`. Give the OAuth suite its own database, because it writes signing keys. Never point test variables at a database with real data. `.github/workflows/e2e.yml` shows the full setup. + +### Browser tests + +End-to-end tests use [Playwright](https://playwright.dev/) and live in `tests/e2e/specs`, with fixtures in `tests/e2e/fixtures`. They cover sign-up and sign-in, section editing and autosave, JSON export and import, public sharing with statistics and passwords, OAuth consent for MCP clients, and the assistant against a scripted AI provider. + +Playwright starts the **built** server (`node apps/server/dist/index.mjs`) in production mode and waits for `/api/health`, so build first. Use a disposable database, and export the variables yourself; these scripts don't load `.env.local`. + +```bash +pnpm exec playwright install chromium + +export APP_URL=http://localhost:3000 PORT=3000 +export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres +export AUTH_SECRET=$(openssl rand -hex 32) ENCRYPTION_SECRET=$(openssl rand -hex 32) +export LOCAL_STORAGE_PATH="$PWD/data/e2e" +export FLAG_DISABLE_SIGNUPS=false FLAG_DISABLE_EMAIL_AUTH=false FLAG_DISABLE_API_RATE_LIMIT=true +export FLAG_ALLOW_UNSAFE_AI_BASE_URL=true # lets the assistant spec reach its local stub + +pnpm db:migrate +pnpm build +pnpm test:e2e # all specs +pnpm test:e2e tests/e2e/specs/auth.spec.ts # one spec +pnpm test:e2e:ui # Playwright's interactive UI +``` + +Without `FLAG_ALLOW_UNSAFE_AI_BASE_URL=true`, the assistant spec skips itself. Playwright runs Chromium with no retries. Locally it reuses a server that's already running on `PORT`. See `tests/e2e/README.md` for the full recipe. + + + Keep the unsafe AI and OAuth redirect flags for isolated test installations. They relax SSRF and redirect protections. + + +## Add translatable text + +The web app uses [Lingui](https://lingui.dev/). Wrap every user-facing string in a macro: ```tsx import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; -// For plain strings -const message = t`Hello, World!`; +const label = t`Download PDF`; -// For JSX content -Welcome to Reactive Resume; +Your resume is ready.; ``` -### Extracting translations +Then run `pnpm lingui:extract`. It updates the catalogs in `apps/web/locales/*.po` and regenerates the PDF section-title catalog. You only add English strings; translators handle the rest on Crowdin. See [Contributing translations](/contributing/translations). -After adding new translatable text, extract them to the locale files: +## Code style -```bash -pnpm run lingui:extract -``` +- TypeScript is strict, including `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`. Packages type-check with `tsgo --noEmit`. +- [Biome](https://biomejs.dev/) formats and lints: tabs, double quotes, 120-column lines, separated type imports, organized imports, and sorted Tailwind classes in `clsx`, `cva`, and `cn`. Set your editor to use Biome. +- React components with explicit props use a named props type, such as `type FooProps = {...}` with `function Foo(props: FooProps)`. -Translation files live in `apps/web/locales`, in `.po` format. +## Commits and pull requests ---- +The Git hooks run automatically: -## Code quality +- **Before each commit**, Lefthook checks staged files for merge conflict markers and runs Biome with `--write --unsafe` on them, then stages the fixes. +- **On each commit message**, commitlint enforces [Conventional Commits](https://www.conventionalcommits.org/), such as `fix(pdf): keep the timeline dot round` or `docs: update the development guide`. -### Linting & formatting +Before you open a pull request: -Uses [Biome](https://biomejs.dev/) for linting, formatting, import organization, and Tailwind class sorting: +1. Run the type checks and tests for the packages you changed, plus a non-mutating Biome check. +2. Run `pnpm exec turbo boundaries` if you changed imports, exports, or shared contracts. +3. Run `pnpm build` if you changed runtime or bundling behavior. +4. Keep the pull request focused. Describe the problem, the new behavior, and the checks you ran, and link the related [GitHub issue](https://github.com/reactive-resume/reactive-resume/issues). -```bash -# Non-mutating check -pnpm exec biome check . +Pull requests run these GitHub Actions workflows: -# Project script with write/fix behavior -pnpm check -``` +| Workflow | What it checks | +| --- | --- | +| `e2e.yml` | Unit tests for every workspace (`turbo run test:ci --concurrency=1`), a production build, and the Playwright suite | +| `vercel.yml` | Builds the Vercel artifact offline and checks the backend Function. See [Deployment checks](/contributing/deployment-checks). | +| `autofix.yml` | Runs `pnpm knip --fix` and `pnpm check`, then pushes any fixes to your branch | -### Type checking - -Run TypeScript type checking: - -```bash -pnpm run typecheck -``` - - - Configure your IDE to use Biome for formatting and lint diagnostics. The repo uses tabs, double quotes, 120-column - lines, and organized import groups. - - ---- +Keep credentials and personal resume data out of code, logs, test fixtures, issues, and pull requests. ## Troubleshooting - The Vite web server uses `PORT` (default `3000`), and the Hono server uses `SERVER_PORT` (default `3001`). - Either stop the conflicting process or choose alternate ports: - ```bash - PORT=3002 SERVER_PORT=3003 pnpm dev - ``` + Stop the other process, or set different values for `PORT` and `SERVER_PORT` in `.env.local` and change `APP_URL` to match. Keep port `3002` free for the email preview. - - Ensure Docker containers are running: - ```bash - docker compose -f compose.dev.yml ps - docker compose -f compose.dev.yml up -d - ``` - Check that PostgreSQL is healthy and accessible on port 5432. + + Check that the containers are healthy with `docker compose -f compose.dev.yml ps`. Code on your machine connects to `localhost`; code inside a container uses the service name, such as `postgres`. - - Verify SeaweedFS is running and the bucket exists: + + Read the storage logs: + ```bash - docker compose -f compose.dev.yml logs seaweedfs - docker compose -f compose.dev.yml logs seaweedfs_create_bucket - ``` - If the bucket wasn't created, restart the bucket creation service: - ```bash - docker compose -f compose.dev.yml restart seaweedfs_create_bucket + docker compose -f compose.dev.yml logs seaweedfs seaweedfs_create_bucket ``` + + Check that `S3_ENDPOINT` is `http://localhost:8333` and the bucket exists. If you don't need S3, set `STORAGE_BACKEND=local`. - - The route tree may need regeneration. Run the dev server which auto-generates routes: - ```bash - pnpm run dev - ``` - Or run type checking to see specific errors: - ```bash - pnpm run typecheck - ``` + + `apps/web/src/routeTree.gen.ts` is generated. Start `pnpm dev` (or run `pnpm build`) to regenerate it, and never edit it by hand. + + Saved AI providers and the assistant need `ENCRYPTION_SECRET` (at least 32 characters). Set it in `.env.local` and restart `pnpm dev`. + + + + CommonJS server dependencies must be bundled. Add the package and its dependencies to `bundledInteropPackages` in `apps/server/tsdown.config.ts`. See [Deployment checks](/contributing/deployment-checks). + ---- - ## Next steps - - How the project and codebase are structured. - - - View the source code and contribute to the project. - + + Learn where each part of the code lives and where new code belongs. + + + Browse the source, open issues, and send pull requests. + diff --git a/docs/contributing/translations.mdx b/docs/contributing/translations.mdx index 519f9ceea..661307980 100644 --- a/docs/contributing/translations.mdx +++ b/docs/contributing/translations.mdx @@ -1,162 +1,118 @@ --- title: "Contributing translations" -description: "Contribute translations for Reactive Resume through Crowdin by joining the project, proposing strings, requesting new languages, and syncing updates." +description: "Help translate Reactive Resume on Crowdin: read the glossary, keep placeholders intact, request a new language, and update catalogs in a checkout." --- -Reactive Resume is used all over the world. If you speak a language other than English, you can help by contributing translations. +Reactive Resume is available in more than 50 languages, and every translation comes from volunteers. If you speak a language other than English, you can help people build their resume in it. You don't need to write code: translations happen in your browser on Crowdin. ---- +## How translations reach the app -## How translations work +1. Developers write every interface string in English. The strings are collected in one source catalog, `apps/web/locales/en-US.po`. +2. Translators work on that catalog in the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume). +3. After each change to the `main` branch, a GitHub workflow uploads the source catalog, downloads the latest translations, and opens a pull request titled "Sync Translations from Crowdin". +4. Once that pull request is merged, your translations ship with the next release of the app. There's usually a delay of a few days to a few weeks, depending on the release cycle. -Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin gives translators an interface for contributing translations without writing code or editing files directly. +The same translations also label the default section headings in downloaded PDFs, such as "Experience" and "Education", and the word "Present" in date ranges. - - The Reactive Resume Crowdin project is available at - [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume). - +## Read the glossary first -Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app. +Most of the interface is made of short, standalone labels like `Board`, `Resume`, or `Check`. Without a sentence around them, it's easy to pick the wrong meaning. For example, **Resume** is always the document, never the verb "to resume". ---- +The [glossary](https://github.com/reactive-resume/reactive-resume/blob/main/GLOSSARY.md) explains what each recurring term means in Reactive Resume, lists the wrong senses that earlier translations used, and names the terms that stay in English: the product name, technology names such as PDF, DOCX, JSON, API, and MCP, AI provider names, and template names such as Azurill and Pikachu. Read the entry for a term before you translate it. -## Updating catalogs in a checkout - -The PDF renderer uses a generated subset of the same translations for default section headings. Run `pnpm pdf:translations` after editing or syncing the PO catalogs. The root `pnpm lingui:extract`, `pnpm check`, and `pnpm build` commands also regenerate this file automatically. - -Commit `packages/pdf/src/section-title-catalog.json` with the catalog updates. Edit the source PO files rather than the generated JSON; a tooling test checks that they stay synchronized. - -## Getting started +## Translate on Crowdin - - If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your - email or sign up with Google, Facebook, Twitter, GitHub, or GitLab. - - For detailed instructions on creating an account and getting started, see Crowdin's official [For - Translators](https://support.crowdin.com/for-translators/) documentation. - + + Sign up at [crowdin.com](https://crowdin.com/) with your email or an existing account such as GitHub or Google. Crowdin's [guide for translators](https://support.crowdin.com/for-translators/) explains the basics. - - Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join** - to become a contributor. - + + Open the [Reactive Resume project](https://crowdin.com/project/reactive-resume) and select **Join**. + - - From the project dashboard, click on the language you want to translate. You'll see a list of files that need - translation along with the progress for each. - - - - Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for - your translation on the right. - Translate the text accurately while preserving any placeholders or formatting - Use - the suggestions from Translation Memory and Machine Translation as a starting point - Vote on existing translations - if you agree with them - - - - Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release. - + + Select your language on the project dashboard to see how much is already translated. + + + Open the file to start the Crowdin editor. The English source is on one side and your translation on the other. Use translation memory and machine suggestions as a starting point, and vote for existing translations you agree with. Crowdin saves your work as you go. + ---- +If a string is unclear, leave a comment on it in Crowdin. You can also search the source code for the English text to see where it appears. ## Translation guidelines -To maintain consistency across all translations, please follow these guidelines: +### Keep placeholders unchanged -### Preserve placeholders - -Some strings contain placeholders like `{name}` or `{count}`. These must remain unchanged in your translation: +Words in curly braces are filled in by the app. Keep them exactly as they are, but move them wherever your grammar needs them. For example: ``` -English: "Hello, {name}!" -Spanish: "¡Hola, {name}!" +English: “{name}” moved to Trash +German: „{name}“ in den Papierkorb verschoben ``` -### Keep formatting +Numbered placeholders such as `{0}` often come with a note in Crowdin, like `placeholder {0}: application.role`, that tells you what the value is. -Preserve any HTML tags or markdown formatting in the source text: +### Keep numbered tags around the same words + +Tags such as `<0>` and `` mark text that gets a link or emphasis. Keep each pair, and wrap the words that carry the same meaning in your language. For example: ``` -English: "Click <1>here to continue" -German: "Klicken Sie <1>hier, um fortzufahren" +English: Have a resume already? <0>Import it +French: Vous avez déjà un CV ? <0>Importez-le ``` -### Use formal or informal tone consistently +### Translate every plural form -Choose either formal or informal language based on what's standard for software in your language, and stick with it throughout. +Some strings change with a number. They use this pattern: -### Technical terms +``` +{0, plural, one {# application ready to import} other {# applications ready to import}} +``` -Some technical terms (like "PDF", "URL", "JSON") are often kept in English across languages. Use your judgment based on what's common in your language's software community. +Translate the text inside each set of braces, keep `#` where the number goes, and don't translate the keywords `plural`, `one`, and `other`. Crowdin shows the plural categories your language needs. ---- +### Be consistent -## Requesting a new language +- Choose a formal or informal tone based on what's normal for software in your language, and use it everywhere. +- Where your language normally calls this document a CV, use CV. +- Reuse the same word for a term throughout. The glossary lists the terms that matter most. -If your language is not listed in the Crowdin project, you can request it to be added. +## Request a new language - - Before requesting a new language, please check if it's already available in the [Crowdin - project](https://crowdin.com/project/reactive-resume). - +First check whether your language is already listed in the [Crowdin project](https://crowdin.com/project/reactive-resume). If it isn't: -To request a new language: +1. Open a new issue on [GitHub](https://github.com/reactive-resume/reactive-resume/issues/new/choose). +2. Title it "Add [language name] translation". +3. Include the language name and its locale code, such as `ja-JP` for Japanese. -1. Go to the [GitHub Issues](https://github.com/reactive-resume/reactive-resume/issues) page -2. Click **New Issue** -3. Select the appropriate template or create a blank issue -4. Title it something like: "Add [Language Name] to Reactive Resume" -5. Include the language name and locale code (e.g., "Japanese - ja-JP") in the issue description. +Once a maintainer adds the language, you can start translating it on Crowdin. -Once approved, the language will be added to Crowdin and you can begin translating. +## Updating catalogs in a checkout ---- +This section is for developers working in the repository. -## When will my translations appear? +After you add or change user-facing strings with Lingui macros, extract them: -Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume. +```bash +pnpm lingui:extract +``` - - There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on - the release cycle. - +This updates every catalog in `apps/web/locales/*.po` and then runs `pnpm pdf:translations`, which regenerates two files from the catalogs: ---- +- `packages/pdf/src/section-title-catalog.json`: default section titles for PDFs. +- `packages/schema/src/resume/present-labels.json`: the translated word for "Present" in date ranges. -## Tips for effective translation +`pnpm check` and `pnpm build` also regenerate them. Commit the generated files together with the catalog changes. Edit the `.po` files, never the JSON; a test in `tooling/locales` fails when the two drift apart. - - - Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app. - - - Review translations by other contributors and vote for accurate ones to help maintain quality. - - - Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors. - - - Check the project glossary (if available) to ensure terminology is used consistently across the app. - - +Only edit `en-US.po` by extracting it from code. Other catalogs arrive through the Crowdin pull request, so change translations on Crowdin rather than in the repository, or your edit is overwritten by the next sync. ---- +To add a new locale, a maintainer adds its code to `locales` in `apps/web/lingui.config.ts`, to `localeSchema` in `packages/utils/src/locale.ts`, and to `localeMap` in `apps/web/src/libs/locale.ts`, then runs `pnpm lingui:extract`. -## Need help? +## Related pages - - - Official Crowdin documentation for translators. - - - Report issues or request new languages. - - - ---- - -Thank you for helping translate Reactive Resume. +- [Changing appearance and language](/guides/changing-appearance-and-language): switch the app's language. +- [Development setup](/contributing/development): run Reactive Resume locally. +- [Crowdin translator docs](https://support.crowdin.com/for-translators/): how the Crowdin editor works. diff --git a/docs/docs.json b/docs/docs.json index 509d71f7d..c1fb9e22d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -12,6 +12,30 @@ { "source": "/guides/semantic-css-reference", "destination": "/applying-custom-styles" + }, + { + "source": "/guides/managing-resumes-from-the-dashboard", + "destination": "/guides/managing-documents" + }, + { + "source": "/guides/moving-items-between-sections", + "destination": "/guides/editing-entries" + }, + { + "source": "/guides/adding-a-cover-letter", + "destination": "/guides/writing-a-cover-letter" + }, + { + "source": "/guides/using-the-builder-dock", + "destination": "/guides/editor-overview" + }, + { + "source": "/guides/using-ai-in-the-builder", + "destination": "/guides/using-the-assistant" + }, + { + "source": "/guides/using-ai-agent", + "destination": "/guides/using-the-assistant" } ], "seo": { @@ -45,6 +69,9 @@ "pages": [ "getting-started", "getting-started/quickstart", + "guides/whats-new-in-v6", + "guides/using-the-command-bar", + "guides/keyboard-shortcuts", "guides/checking-service-status", "guides/accessing-the-previous-version" ] @@ -53,56 +80,93 @@ "group": "Account", "pages": [ "guides/creating-an-account", + "guides/signing-in", "guides/updating-your-profile", + "guides/changing-appearance-and-language", "guides/linking-social-accounts", + "guides/setting-up-two-factor-authentication", + "guides/setting-up-passkeys", + "guides/exporting-your-data", "guides/deleting-your-account" ] }, { - "group": "Resume Builder", + "group": "Documents", "pages": [ "guides/creating-your-first-resume", - "guides/managing-resumes-from-the-dashboard", - "guides/importing-resumes", - "guides/choosing-a-template", - "guides/selecting-page-format", - "guides/moving-items-between-sections", - "guides/fitting-content-on-a-page", - "guides/adding-a-cover-letter", - "guides/using-the-builder-dock", - "guides/undoing-changes-and-version-history", - "applying-custom-styles", - "guides/using-the-ats-checker", - "guides/using-ai-in-the-builder", - "guides/using-ai-agent", - "guides/using-private-notes", - "guides/exporting-your-resume", - "guides/exporting-resume-to-markdown", - "guides/sharing-your-resume-publicly" + "guides/managing-documents", + "guides/organizing-with-tags", + "guides/using-the-trash", + "guides/importing-resumes" ] }, + { + "group": "Resume Editor", + "pages": [ + "guides/editor-overview", + "guides/filling-in-your-details", + "guides/formatting-text", + "guides/managing-sections", + "guides/editing-entries", + "guides/entering-dates", + "guides/undoing-changes-and-version-history", + "guides/using-private-notes", + "guides/editing-on-mobile" + ] + }, + { + "group": "Design", + "pages": [ + "guides/choosing-a-template", + "guides/customizing-typography", + "guides/choosing-colors", + "guides/selecting-page-format", + "guides/arranging-the-layout", + "guides/fitting-content-on-a-page", + "applying-custom-styles" + ] + }, + { + "group": "Checking Your Resume", + "pages": ["guides/checking-your-resume", "guides/using-the-ats-checker"] + }, + { + "group": "Cover Letters", + "pages": ["guides/writing-a-cover-letter"] + }, + { + "group": "Sharing and Exporting", + "pages": [ + "guides/sharing-your-resume-publicly", + "guides/exporting-your-resume", + "guides/exporting-resume-to-markdown" + ] + }, + { + "group": "AI Assistant", + "pages": ["guides/using-ai", "guides/using-the-assistant", "guides/ai-agent-tools"] + }, { "group": "Application Tracker", "pages": [ "guides/tracking-job-applications", - "guides/importing-applications-from-csv", - "guides/managing-applications-with-mcp" + "guides/adding-an-application", + "guides/managing-an-application", + "guides/scheduling-interviews", + "guides/tailoring-a-resume-for-a-job", + "guides/viewing-application-insights", + "guides/importing-applications-from-csv" ] }, - { - "group": "Security", - "pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"] - }, { "group": "Integrations", "pages": [ "guides/using-the-api", - "guides/large-rpc-requests", "guides/using-the-patch-api", + "guides/json-resume-schema", "guides/using-the-mcp-server", - "guides/using-ai", - "guides/ai-agent-tools", - "guides/json-resume-schema" + "guides/managing-applications-with-mcp", + "guides/large-rpc-requests" ] }, { @@ -143,20 +207,22 @@ "group": "Self-Hosting", "pages": [ "self-hosting/docker", - "self-hosting/vercel", - "self-hosting/kubernetes", + "self-hosting/environment-variables", "self-hosting/examples", + "self-hosting/kubernetes", + "self-hosting/vercel", "self-hosting/sso", + "self-hosting/upgrading-to-v6", "self-hosting/migration" ] }, { "group": "Contributing", "pages": [ - "contributing/architecture", "contributing/development", - "contributing/deployment-checks", - "contributing/translations" + "contributing/architecture", + "contributing/translations", + "contributing/deployment-checks" ] }, { diff --git a/docs/getting-started.mdx b/docs/getting-started.mdx index 907f39ae9..b54ea9d33 100644 --- a/docs/getting-started.mdx +++ b/docs/getting-started.mdx @@ -1,114 +1,89 @@ --- -title: "Introduction to Reactive Resume" -description: "Reactive Resume is a free, open-source resume builder that lets you create, update, export, and share professional resumes without accounts or paywalls." +title: "Introduction" +description: "Reactive Resume is a free, open-source resume builder. Write resumes and cover letters, track job applications, and share or download your work." --- - - Reactive Resume Banner +Reactive Resume is a free and open-source resume builder. You write your resume on one side of the screen and see the finished page on the other, then download it as a PDF or share it with a link. There are no ads, no tracking and no paid tier. + + + The Reactive Resume editor with a game developer's resume. The Basics card with name, headline, email and phone fields is on the left, and the rendered resume page with a photo, contact line, Profiles, Skills, Summary and Education is on the right. -## What is Reactive Resume? - -Reactive Resume is a free and open-source resume builder that makes it easy to create, update, and share your resume. Built with privacy as a core principle, it gives you complete control over your data. +## What you can do with it - - Your data stays yours. No tracking, no ads, and an open-source codebase you can read. + + Keep every resume and letter in **Documents**. Start blank, from a sample, or by importing a PDF, Word file, JSON + or LinkedIn export. - - Choose from a set of professionally designed templates. + + Pick from 15 templates, then adjust fonts, colors, page size and layout. The page updates as you type. - - See changes instantly as you type. What you see is exactly what you'll get when you export. + + **Check** mode points out issues on the lines they belong to and compares your resume with a job posting. - - Download your resume as PDF, share it via a unique link, or print it directly from your browser. + + Download a PDF, Word, Markdown or JSON file, or publish a link that you can protect with a password. + + + Follow each application from saved to offer in a list, board or calendar, and see what's working in Insights. + + + Connect your own AI provider, and the assistant proposes edits you accept or reject one by one. -## Key features +## Who it's for - - An infographic of the major features of Reactive Resume - +Reactive Resume is for anyone applying for jobs. You don't need design skills or an account with any other service. +The app is available in 55 languages, works on phones, tablets and computers, and has light and dark themes. - - - Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The - entire codebase is available on [GitHub](https://github.com/reactive-resume/reactive-resume). - +You can use it in two ways: - - Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditgar, Ditto, - Gengar, Glalie, Kakuna, Lapras, Leafish, Meowth, Onyx, Pikachu, Rhyhorn, and Scizor, each with its own layout and style. - +- **The hosted version at [rxresu.me](https://rxresu.me).** Create a free account and start writing. This is the + right choice for most people. +- **Your own copy.** Reactive Resume is open source under the MIT license, so you or your organization can run it on + your own server with Docker. Self-hosted copies have the same features as the hosted version. - - Format your content with bold, italic, links, lists, and more using the rich text editor, powered by Tiptap. - +## Your data - - Reactive Resume is available in multiple languages. Contribute translations to help us reach more people. - +Your resumes belong to you. You can download any document as JSON and import it back later, export everything in your +account from **Settings**, and delete your account at any time. The source code is public on +[GitHub](https://github.com/reactive-resume/reactive-resume), so anyone can check what the app does with your data. - - Built-in dark mode support, so you can work comfortably in any lighting condition. - - - - Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure. - - - - -## Getting started - -Use the hosted version, or run your own instance. +## Get started - Start with the hosted version, or deploy your own instance. + Create an account, build a resume from a sample and download your first PDF in about ten minutes. - - Set up a local development environment to contribute or customize Reactive Resume. + + Used Reactive Resume before? See what changed and where things moved. + + + Run Reactive Resume on your own server. + + + Set up a development environment to fix bugs or add features. -## Tech stack - -| Category | Technology | -| ---------------- | ------------------------------- | -| Framework | TanStack Start (React 19, Vite) | -| Runtime | Node.js | -| Language | TypeScript | -| Database | PostgreSQL with Drizzle ORM | -| API | ORPC (Type-safe RPC) | -| Auth | Better Auth | -| Styling | Tailwind CSS | -| UI Components | Base UI + shadcn-style package | -| State Management | Zustand + TanStack Query | - -## Community & support +## Get help - - Star the repo, report reproducible bugs, propose features, and contribute to the project. - - Ask questions about setup, configuration, and using Reactive Resume. + Ask questions about using or hosting Reactive Resume. - - Get help and talk to other users on our subreddit. + + Report a bug you can reproduce, or propose a feature. - Get help and talk to other users on our Discord server. + Talk to other people who use Reactive Resume. - - Help fund the continued development of Reactive Resume. + + Share tips and ask for help on the subreddit. - - **Need help?** Start a [GitHub Discussion](https://github.com/reactive-resume/reactive-resume/discussions/categories/q-a). - GitHub Issues are reserved for reproducible bugs and actionable feature proposals. - +Reactive Resume is kept free by donations. If it helps you, consider +[supporting the project on Open Collective](https://opencollective.com/reactive-resume/donate). diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 5eb3b42ae..d69e8b272 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -1,237 +1,151 @@ --- title: "Quickstart" -description: "Sign in to the hosted version of Reactive Resume, or deploy your own self-hosted instance with Docker Compose." +description: "Create a free account, build a resume from the sample, make it yours, try a template and download your first PDF in about ten minutes." --- -## Options +In this tutorial you'll create a Reactive Resume account, open a sample resume, put your own name on it, try a different +template and download it as a PDF. It takes about ten minutes, and at the end you'll know your way around the editor. -There are two ways to use Reactive Resume: +You only need a web browser. The steps use the hosted version at [rxresu.me](https://rxresu.me); if your organization +runs its own copy, use its address instead. - - - The fastest way to get started, and the right choice for most people. - - - Deploy your own instance and keep full control. Requires some technical knowledge. - - - ---- - -## Using the cloud version - -The easiest way to use Reactive Resume is our cloud version at [rxresu.me](https://rxresu.me). It is free, and it will stay free. +## 1. Create your account - - Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google - account. - - - - Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started. - - - - Use the builder to add your personal information, work experience, education, skills, projects, and - anything else the resume needs. - - - - When you're ready, export your resume as a PDF or share it at its public URL. - - + + Go to [rxresu.me/auth/register](https://rxresu.me/auth/register). + + + Enter your **Name**, a **Username**, your **Email Address** and a **Password**, then select **Sign up**. If you'd + rather use an existing Google, GitHub or other account, pick it below the form instead. + + + Reactive Resume sends you an email to verify your address. Verifying is optional, but you need it to reset a + forgotten password. Select **Continue** to carry on now and verify later. + -Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look. +You land on **Documents**, the home page that holds all your resumes and cover letters. It's empty for now. ---- + + The empty Documents page with the heading "Let's start with what you have", a drop zone for a PDF, Word or JSON file with a Choose a file button, and Start blank and Try a sample links underneath. + -## Self-host with Docker +## 2. Open the sample resume -You can deploy Reactive Resume on your own infrastructure using Docker. +Select **Try a sample**. Reactive Resume creates a complete resume for a game developer and opens it in the editor. - - **From v5.1.0 onwards** — PDF generation now runs entirely client-side with the Forme PDF engine (WebAssembly). Self-hosted deployments no longer require Browserless, Chromium, or any external print service as a dependency. The `PRINTER_*` and `BROWSERLESS_*` environment variables are no longer read and can be removed from your `.env`. - +Starting from the sample lets you see what a finished resume looks like before you replace its content with yours. +The editor has two halves: -### Prerequisites +- **The panel** on the left, where you write. It starts with the **Basics** card (your name and contact details), + followed by your sections, such as Summary, Education and Experience. +- **The page** on the right, which shows exactly what your PDF will look like. It updates as you type. -Before you begin, ensure you have the following installed: +The sample's document name and the name on the resume are a random three-word phrase, such as "Slippery Blush +Marlin". You'll fix both next. -- [Docker](https://docs.docker.com/get-docker/) (v20.10 or higher) -- [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher) +## 3. Name your document - - There is no difference in features between the cloud-hosted version and the self-hosted option. Both - offer the same privacy and customization. Pick whichever deployment type suits you. - - -### Quick deployment +The document name is only for you; it's how you find the resume in **Documents**. - - ```bash - git clone https://github.com/reactive-resume/reactive-resume.git reactive-resume - cd reactive-resume - ``` - - - - Create a `.env` file in the root directory with the following variables: - -```bash .env -# Application -APP_URL=http://localhost:3000 - -# Database -DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres - -# Authentication (generate a secure secret) -AUTH_SECRET=your-secure-secret-key-here - -# Storage (S3-compatible via SeaweedFS) -S3_ACCESS_KEY_ID=seaweedfs -S3_SECRET_ACCESS_KEY=seaweedfs -S3_ENDPOINT=http://seaweedfs:8333 -S3_BUCKET=reactive-resume -S3_FORCE_PATH_STYLE=true - -# AI features (optional; ENCRYPTION_SECRET for saved providers, plus REDIS_URL for the agent) -REDIS_URL=redis://redis:6379 -ENCRYPTION_SECRET=your-secure-encryption-secret-here -``` - - - For production deployments, always use strong, unique values for `AUTH_SECRET`, `ENCRYPTION_SECRET`, and - database credentials. - - - - - ```bash - docker compose up -d - ``` - - This starts: - - **PostgreSQL** — Database for storing user data and resumes - - **Redis** — Required for the AI Agent workspace - - **SeaweedFS** — S3-compatible storage for file uploads - - **Reactive Resume** — The main application - - - - Once all services are running, access your Reactive Resume instance at: - - ```text - http://localhost:3000 - ``` - - + + Select the document name at the top left of the editor. + + + Select **Rename…**, type a name such as "Game Developer Resume" in **Name**, then select **Save Changes**. + -### Docker Compose services +## 4. Put your name on the page -Here's what each service in the stack does: + + + In the **Basics** card, replace the text in **Full name** with your own name. + + + The name at the top of the page changes as you type. Try changing **Headline** to the job title you want too. + + -| Service | Port | Description | -| ----------------- | ---- | ---------------------------------------------------- | -| `postgres` | 5432 | PostgreSQL database for storing all application data | -| `redis` | 6379 | Redis instance required by the AI Agent workspace | -| `seaweedfs` | 8333 | S3-compatible object storage for file uploads | -| `reactive_resume` | 3000 | The main Reactive Resume application | + + The Basics card with Full name set to David Kowalski and a headline, email, phone and location, next to the resume page showing the same name and contact details under a photo. + - - Saved AI provider management requires `ENCRYPTION_SECRET`, and the AI Agent workspace requires both `REDIS_URL` and - `ENCRYPTION_SECRET`. Other Reactive Resume features can run without them. Agent attachments and other private objects - require S3-compatible storage; local storage rejects private objects. - +You don't need to save. Under the document name, the status changes to **Saving…** and then **Saved**. -### Health checks + + Made a mistake? Press ⌘ Z (Ctrl Z on Windows and Linux) to undo it. + -All services include health checks. To verify that everything is running: +## 5. Try another template -```bash -docker compose ps -``` + + + Select **Design** at the top of the editor, or press 2. + + + **Template**, at the top of Design, shows all 15 templates filled with your own content. Select one, such as + **Bronzor**, and the page switches to it. Your content stays the same; only the look changes. + + + Select **Write**, or press 1, to return to your content. + + -You should see all services with a `healthy` status. + + The Template tab in Design mode with filters for All, One column, Two columns and ATS-safe, the text "15 of 15 shown", and previews of the Azurill (selected), Bronzor, Chikorita and Ditgar templates filled with the sample resume. + ---- +## 6. Download your PDF -## Environment variables reference + + + Select **Download PDF** at the top right of the editor. Your browser saves the file, named after the name on your + resume (for example, `David-Kowalski-Resume.pdf`). + + + Open the PDF. It matches the page you saw in the editor. + + -A complete list of the environment variables you can configure: + + The right side of the editor bar with the History and Assistant icon buttons, a Share button and a green Download PDF button with a small arrow on its right. + -### Required variables +To download a different format, select the arrow next to **Download PDF**. The **Share & export** panel opens on its +**Download** tab, where you can choose **PDF**, **Word**, **Markdown** or **JSON** and change the file name. -| Variable | Description | Example | -| -------------- | ------------------------------ | ------------------------------------- | -| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` | -| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -hex 32` | -| `APP_URL` | Public URL of your Application | `https://rxresu.me` | + + The Share and export panel with Link, Download and History tabs. The Download tab lists PDF (Best for applying), Word, Markdown and JSON, a File name field set to David-Kowalski-Resume, a note that Check has 3 things to review, and a Download PDF button. + -### Optional variables +## What you've done -| Variable | Description | Default | -| ------------------------------------- | ----------------------------------------------------------- | ---------------------- | -| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — | -| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — | -| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — | -| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — | -| `LINKEDIN_CLIENT_ID` | LinkedIn OAuth Client ID | — | -| `LINKEDIN_CLIENT_SECRET` | LinkedIn OAuth Client Secret | — | -| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — | -| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — | -| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — | -| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — | -| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — | -| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — | -| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — | -| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` | -| `BETTER_AUTH_API_KEY` | Better Auth dashboard API key | — | -| `SMTP_HOST` | SMTP Server Host (for email features) | — | -| `SMTP_PORT` | SMTP Server Port | `587` | -| `SMTP_USER` | SMTP Username | — | -| `SMTP_PASS` | SMTP Password | — | -| `SMTP_FROM` | Default FROM address for emails | — | -| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` | -| `S3_ACCESS_KEY_ID` | S3 Access Key | — | -| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — | -| `S3_REGION` | S3 Region | `us-east-1` | -| `S3_ENDPOINT` | S3-compatible Endpoint URL | — | -| `S3_BUCKET` | S3 Bucket Name | — | -| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` | -| `REDIS_URL` | Redis connection string for the AI Agent workspace | — | -| `ENCRYPTION_SECRET` | Encryption secret for saved AI provider credentials | — | -| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` | -| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` | -| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` | -| `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` | Allows arbitrary dynamic OAuth redirect URIs | `false` | -| `FLAG_ALLOW_UNSAFE_AI_BASE_URL` | Allows unsafe/private/non-public AI provider base URLs | `false` | - -> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused. - -> **AI features:** Saved AI provider management requires `ENCRYPTION_SECRET`, and the AI Agent workspace requires both `REDIS_URL` and `ENCRYPTION_SECRET`. Live web research depends on the selected AI provider/model supporting native web search. Keep `FLAG_ALLOW_UNSAFE_AI_BASE_URL` disabled unless this is a trusted self-hosted deployment; public HTTPS provider URLs are the safe default. - -> **OAuth redirect safety:** Keep `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` disabled unless this is a trusted self-hosted deployment. Enabling it allows dynamic OAuth clients to register any parseable redirect URI, including custom schemes, private hosts, and non-loopback `http://` URLs, which can enable phishing or token exfiltration on public or multi-tenant instances. - -> **Health check behavior:** `/api/health` reports status for database and storage. A failure in either dependency returns HTTP `503`. - ---- +You created an account, built a resume from the sample, made it yours, tried a template and downloaded a PDF. From +here, replace the rest of the sample with your own experience, or start a fresh resume from **New** in the sidebar +(press N). ## Next steps - - Set up a development environment to contribute or customize Reactive Resume. + + Start from scratch, or import the resume you already have. - - Learn about the project structure and architecture. + + Learn the editor bar, the three modes and the page view. + + + Fix the issues Check finds and compare your resume with a job posting. + + + Publish a link that recruiters can open in their browser. - - **Having trouble?** Check our [GitHub Issues](https://github.com/reactive-resume/reactive-resume/issues) or reach out via - [email](mailto:hello@amruthpillai.com). - +## Run your own copy + +Want to host Reactive Resume on your own server instead? Follow [Self-hosting with Docker](/self-hosting/docker); the +[environment variables reference](/self-hosting/environment-variables) lists every setting. Developers who want to +change the code should start with the [development setup](/contributing/development). diff --git a/docs/guides/accessing-the-previous-version.mdx b/docs/guides/accessing-the-previous-version.mdx index 56a66f874..ce97eca54 100644 --- a/docs/guides/accessing-the-previous-version.mdx +++ b/docs/guides/accessing-the-previous-version.mdx @@ -1,80 +1,71 @@ --- title: "Accessing the previous version" -description: "Access the previous version (v4) of Reactive Resume to retrieve old resumes, and export them for import into the latest version." +description: "Your v5 resumes carried over to Reactive Resume v6. Learn how to move resumes from a v5 or v4 copy into v6 by exporting and importing JSON." --- -## Check whether the previous version is available +Reactive Resume v6 replaced v5 on rxresu.me. This page explains what happened to your v5 work, and how to bring resumes +over from an older copy of Reactive Resume (v5 or v4) that you still have access to. -If you've used Reactive Resume for a while, you may have resumes saved in version 4 (v4). Access depends on whether -your self-hosted instance or the hosted previous-version service is currently available. +## Your v5 work is already in v6 - - When the hosted previous version is available, its address is - [https://v4.rxresu.me](https://v4.rxresu.me). Availability is not guaranteed. - +If you used v5 on rxresu.me, you don't need to do anything. Your account, resumes, cover letters and applications moved +to v6 automatically. Sign in at [rxresu.me](https://rxresu.me) as before to find them in **Documents** and +**Applications**. -Self-hosted operators control their own v4 instance and backups. The [v4 to v5 migration -guide](/self-hosting/migration) applies only to infrastructure they are authorized to operate. It does not authorize -access to hosted databases or backups. +A few things look different after the move: -## When v4 is accessible +- Cover letters that were sections inside a resume are now separate letters in **Documents**, linked to that resume. +- Dates were converted to months and years. Any date that couldn't be read exactly still prints as you wrote it. -Open, export, and securely back up each resume you need. Import the export into v5 as a new resume; keep the v5 version -until you have compared both copies. +[What's new in v6](/guides/whats-new-in-v6) explains these and the other changes. -## Accessing your v4 resumes +## Is v5 still available? + +There's no hosted copy of v5 that you can count on. If you need a resume exactly as it was in v5, your best option is a +JSON file you downloaded from v5 earlier, or a v5 copy that you or your organization runs. + +People who host Reactive Resume themselves can keep running v5 by pinning the `v5` image tag (for example, +`amruthpillai/reactive-resume:v5`) on a database that hasn't been upgraded. v5 isn't meant to run on a database that v6 +has already upgraded, so back up the database first. Server owners should read +[Upgrading to v6](/self-hosting/upgrading-to-v6) before they update. + +## Move a resume from v5 into v6 + +If you have access to a v5 copy, export each resume there and import it into v6. - - Go to [https://v4.rxresu.me](https://v4.rxresu.me) in your browser. + + In v5, open the resume and download it as **JSON**. Repeat for each resume you want to keep. - - - Use the same account credentials you used when you originally created your resumes in v4. - - - If you used social sign-in (Google, GitHub, etc.) in v4, use the same method to sign in. - - + + Sign in at [rxresu.me](https://rxresu.me) (or your own address) and select **New** in the sidebar, or press + N. - - - If the dashboard contains your resumes, export each one as JSON before making more changes. + + Select **Import a resume** and choose the JSON file. Reactive Resume recognizes the format and shows how many + sections and entries it found. Select **Open in editor**. + + + The editor tells you what it imported, including any dates to check. If the resume contained a cover letter, it's + saved as a separate letter in **Documents**. -## Migrating to the new version +The import always creates a new resume. It never replaces one you already have, so compare the two before deleting +either. For more on importing, including other file types, see [Importing resumes](/guides/importing-resumes). -If you'd like to move your resumes to the latest version of Reactive Resume, you can export them from v4 and import them into the new version: +## Resumes from v4 -1. In v4, open the resume you want to migrate -2. Export it as a JSON file -3. In the new version at [https://rxresu.me](https://rxresu.me), create a new account or sign in -4. Use the import feature to upload your JSON file (select the "Reactive Resume v4 (JSON)" option) +Version 4 is two versions old. At the time of writing, a copy may still be reachable at +[v4.rxresu.me](https://v4.rxresu.me), but its availability isn't guaranteed. If you can sign in there, open each resume, +export it as JSON, and import it into v6 as described above. v6 recognizes v4 files automatically. - - Import creates a separate resume. It should not be used to replace a newer v5 copy until you have compared both - versions. - +Server owners moving a self-hosted v4 installation should follow the [v4 to v5 migration guide](/self-hosting/migration) +first, then [Upgrading to v6](/self-hosting/upgrading-to-v6). -## When hosted v4 or a resume is unavailable +## If something is missing -Only an authorized hosted service operator can determine whether a source snapshot exists. Open a GitHub issue without -including resume contents, account credentials, reset links, or other private data. A useful request identifies the -approximate time of the missing edits, the sign-in method, and whether the resume is missing or merely not visible. - -Recovery is handled per owner. Before accessing content, the operator must record a private case with source snapshot -time, owner verification, source-to-target mapping, target resume ID, content hashes, and proposed outcome. A matching -email address, username, or resume title alone is not proof of ownership. - -Default recovery result is a private JSON export delivered through an approved channel to a verified recipient. Old-only -or divergent content must remain a separate copy; it must not overwrite a current v5 resume. If no source snapshot is -available, the factual outcome is that the records cannot be recovered from the service. Local tooling cannot recreate -missing source data. - -An empty workspace with successful create responses or name conflicts can instead be a listing or account-mapping -problem. That requires a separate session, create, list, and reload diagnosis; a v4 recovery export does not resolve it. - -## Questions or issues? - -If you run into problems accessing v4, or have questions about migrating your resumes, open an issue on [GitHub](https://github.com/reactive-resume/reactive-resume/issues). +If a resume you expect is missing from v6, ask for help in +[GitHub Discussions](https://github.com/reactive-resume/reactive-resume/discussions/categories/q-a). Describe what's +missing and roughly when you last edited it. Don't post resume contents, passwords, reset links or other private +details. diff --git a/docs/guides/adding-a-cover-letter.mdx b/docs/guides/adding-a-cover-letter.mdx deleted file mode 100644 index 2f3883250..000000000 --- a/docs/guides/adding-a-cover-letter.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: "Adding a cover letter" -description: "Write a cover letter in Reactive Resume as a document of its own, match it to your resume's design, attach it to a job application, and export it." ---- - -A cover letter goes alongside your resume when you apply for a job. In Reactive Resume a cover letter is a document of its own, next to your resumes in **Documents**. It is not a section inside a resume. - -## Create a cover letter - - - - In **Documents**, click **New**, then **New cover letter instead**. - - - - Under **From**, choose the resume the letter goes with. The letter uses that resume's name, contact details and design, and follows them when you change the resume. - - - - Under **For**, link the job application the letter is for. The recipient fills in from the application, and the application lists the letter as the one you sent. - - - - Write the body. The greeting and sign-off follow the recipient's name and your name. - - - -You can also start a letter from an application: open it in **Applications** and click **Write a letter** under **What you sent**. - -## Match your resume, or design it on its own - -By default a letter matches its resume's template, type and colors. Turn off **Match** in the letter's **Design** mode to give it a template, type, colors and page settings of its own. - -## Attach a letter to an application - -A letter linked to an application under **For** is that application's letter. Each application has one letter; linking a letter to another application moves it there. When the application reaches **Applied**, the letter is saved in its History as the version you sent. - -## Export a letter - -Open the letter and click **Share & export**, then **Download**, and pick PDF or DOCX. When you download a resume whose application has a letter, the resume's **Download** tab offers to include the letter as a second file. - -## Letters written inside a resume - -Older versions of Reactive Resume let you add a cover letter as a section of a resume. Those letters have moved to **Documents**: - -- Each one is a letter of its own, named after its resume and section, for example "Frontend Resume — Cover Letter". -- It stays linked to that resume's details and design, so it looks as it did. -- If the resume was used by one application that had no letter, the letter is attached to that application. -- The resume no longer contains the letter. Older versions of the resume in its History still do; restoring one saves those letters again as letters. - -Importing a resume file that contains cover letters, or sending one through the API, does the same: the letters are saved as letters and the resume keeps none. - -## Tips for effective cover letters - -**Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters. - - - **Tailor each letter**: Write one letter per application. Reference specific job requirements and company values. - - - - **Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before - exporting. - diff --git a/docs/guides/adding-an-application.mdx b/docs/guides/adding-an-application.mdx new file mode 100644 index 000000000..93702be9f --- /dev/null +++ b/docs/guides/adding-an-application.mdx @@ -0,0 +1,59 @@ +--- +title: "Adding an application" +description: "Add a job to Applications by pasting its link or posting. Reactive Resume saves the posting so Check, the assistant and your letter can use it." +--- + +Add an application as soon as you find a job you like, or right after you apply. You paste the job link or the posting text, check the role and company, pick a stage, and you're done. The posting is saved with the application, so [Check](/guides/checking-your-resume), [the assistant](/guides/using-the-assistant) and [your cover letter](/guides/writing-a-cover-letter) can refer to it later. + +## Add an application + + + + In **Applications**, select **Add application**. You can also press ⌘ K (Ctrl K on Windows and Linux) and run **New Application**. + + + Paste into **Job link or posting text**. A single web address counts as a link; anything else counts as posting text. Reactive Resume starts reading it a moment after you stop typing. The line under the field tells you what it found. + + + **Role** and **Company** fill in from what was found. Correct them if needed, or type them yourself. Both are required. + + + Choose **Saved**, **Applied** (the default) or **Interview**. You can move the application to any stage later. + + + Select **Add** to save it and open its details. Or select **Add and tailor a resume** to save it and go straight to making a copy of your resume for this job. See [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job). + + + + + Add an application dialog with posting text pasted, a hint to fill in the role and company, Role set to Level Designer, Company set to Brightline Studios, the Stage control on Applied, and the Add and Add and tailor a resume buttons + + +## What gets read from a posting + +What Reactive Resume can read depends on what you paste and whether you've [connected an AI provider](/guides/using-ai): + +| You paste | Without an AI provider | With an AI provider | +| --- | --- | --- | +| A job link | Role, company and location, if the page includes structured job details. Many job boards do. | Role, company, location, salary and a short list of what the job asks for. | +| Posting text | Nothing is read. Fill in the role and company yourself. | Role, company, location, salary and a short list of what the job asks for. | + +Either way, the posting text is saved with the application (up to 20,000 characters). For a link, Reactive Resume saves the page's text and keeps the link. + + + Only public `https://` pages can be read. If a link can't be read, for example because the page needs you to sign in, you see "That link couldn't be read. Paste the posting text instead." Copy the posting from the page and paste it instead. + + +## Add more details + +The Add dialog keeps things short on purpose. Once the application is saved, its details sheet opens, where you can add the salary, source, contacts, tags, notes and a follow-up date. To edit everything in one form, including the job posting link, location and linked resume, open the **⋯** menu in the details sheet and select **Edit details…**. See [Managing an application](/guides/managing-an-application). + +## Other ways to add applications + +- **From a spreadsheet**: import many at once from a CSV file. See [Importing applications from CSV](/guides/importing-applications-from-csv). +- **From an AI client**: create applications over MCP. See [Managing applications with MCP](/guides/managing-applications-with-mcp). + +## Next steps + +- [Managing an application](/guides/managing-an-application): move it through stages and keep notes. +- [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job): make a version of your resume for this posting. diff --git a/docs/guides/ai-agent-tools.mdx b/docs/guides/ai-agent-tools.mdx index 0553ef6b5..fa7cc9f0f 100644 --- a/docs/guides/ai-agent-tools.mdx +++ b/docs/guides/ai-agent-tools.mdx @@ -1,88 +1,76 @@ --- -title: "AI Agent tools" -description: "Reference for the tools the Reactive Resume AI Agent can call to read, edit, patch, and preview resume drafts inside an isolated workspace." +title: "Assistant tools" +description: "Reference for the tools the Reactive Resume assistant can call: reading your document and attachments, proposing edits, asking questions and searching the web." --- -The AI Agent workspace has a fixed set of tools it can use while it chats with you. You do not call these tools directly. The agent picks them when your request needs resume data, supported provider web context, attachments, questions, or a resume patch. +The assistant works through a small, fixed set of tools. You never call them yourself: the assistant picks one when your request needs it, and the conversation shows a short status line for each step. This page lists every tool, what it does, and when it's available. -## Tool activity in chat - -Tool activity appears inside the conversation. Some activity is collapsed by default so the chat stays readable. - -Applied resume patches are shown as a small inline **Patch applied** item. Open it to inspect the raw JSON payload. - - - AI Agent chat showing an applied patch with raw JSON details + + A request to tighten the summary, followed by the status line Read the resume with a checkmark and the start of a card titled 1 of 1 applied -## Available tools +## Tools -| Tool | What it does | Example request | -| --- | --- | --- | -| `read_resume` | Reads the current AI draft and gives the agent the resume data it can safely edit. | "What are the weakest parts of this resume?" | -| `web_search` | Uses the selected provider's native web search when that provider/model supports it. | "Research this company and adjust the summary for its product area." | -| `read_attachment` | Reads extracted text from attached plain text, Markdown, or JSON files. Other supported attachments, such as images or PDFs, are passed to the model when the selected provider can use them. | "Use the attached notes to update the keywords." | -| `ask_user_question` | Shows a question card with answer choices when the agent needs your decision. | "Ask me before changing the career narrative." | -| `apply_resume_patch` | Applies a JSON Patch to the AI draft and stores a rollback snapshot. | "Change the visible name to Amruth Pillai." | +| Tool | What it does | Shown in the conversation as | Available when | +| --- | --- | --- | --- | +| `read_resume` | Reads the open resume: its full text, plus every passage an edit can target (each paragraph of the summary, each bullet or paragraph of an entry's description), each with an ID. Hidden sections and entries are left out. | **Read the resume** | You're in a resume and the document chip is included in your message. | +| `read_letter` | Reads the open cover letter's body, sender and recipient, plus the text of the resume it's linked to, if any. The greeting and sign-off aren't editable through the assistant. | **Read the letter** | You're in a letter and the document chip is included in your message. | +| `propose_edits` | Proposes a set of up to 12 edits with a short title. Each edit either rewrites one passage or adds a new passage after it, and comes with a one-line reason. Nothing changes until you accept. | **Preparing edits…**, then the proposed edits card | Same as the read tool. | +| `ask_user_question` | Asks you a short question, with up to four suggested answers, before the assistant continues. | A blue question card | Always. | +| `read_attachment` | Reads a file attached to a message. Text, Markdown and JSON files return their content (up to 40,000 characters). | **Read the attachment** | Always; useful only when the message has attachments. | +| `web_search` | Searches the web through the provider's own search tool and returns sources. | **Searched the web**, with **Sources** under the reply | Only with an OpenAI provider on its default base URL and a supported model. | -## Resume patches +## How edits are placed -Resume patches are rooted at the resume data object. For example, the visible resume name is patched at `/basics/name`. +Every edit targets one passage by the ID the read tool returned. When you accept, the new text replaces that passage (or is added after it) in your document, as a single step you can undo. -When a patch is applied: +If you or another change edits a passage after the assistant read it, the edit can no longer be placed. The card then says, for example, "1 edit couldn't be placed: its text changed. Ask again to redo it." Edits that were placed but whose passage changed afterwards show **Out of date: the text has changed since.** -- the AI draft updates immediately; -- the raw JSON Patch is available from the **Patch applied** details; -- a snapshot is stored so the draft can be restored to the state before that patch; -- the resume preview refreshes to show the updated draft. +The assistant is instructed to rewrite only what your document already says: it doesn't add employers, titles, dates, numbers, skills or achievements that aren't in the document or the conversation. When a job posting asks for something your document doesn't mention, it uses `ask_user_question` first, and only drafts it from your answer. -Restoring an older patch rolls back that patch and any patches applied after it. If the resume changed after the latest agent patch, restore or apply can fail with a version conflict. In that case, ask the agent to retry from the latest draft. +## What the assistant also sees -## Web access +Besides what the tools return, each message can include: -Live web research is handled only by the selected AI provider's native web search tool. When the provider/model supports it, the agent can use `web_search` for current company, industry, role, or URL-based context. +- **The job posting**: the role, the company, the job description (or its requirements list) and your notes from the linked application. A resume uses the application it was made for, or else the most recently updated application it's linked to. A letter uses only the application it was made for. Removing the posting chip leaves it out. +- **Attachments**: images, PDFs and MP3 or WAV audio are passed to the model directly, when the provider supports them. Other types are only listed by name. +- **The conversation so far**: earlier messages, tool results and your answers. -Provider-native `web_search` is not JSearch and does not restore the removed structured job-listings experience. It supplies web context to the agent; it does not provide a structured job-results API or guarantee that a provider/model can search. +## Web search support -When the provider/model does not support native web search, the agent still works for normal resume editing. If you ask it to browse, search the web, fetch a URL, or use current online context, it should tell you that live web research is unavailable with the selected provider/model and ask you to paste or attach the relevant content instead. +`web_search` uses OpenAI's native search, so it's only offered when all of these are true: -For a controlled workflow that works without live research, follow [Tailor a resume to a supplied job description](/guides/using-ai-agent#tailor-a-resume-to-a-supplied-job-description). Configure, test, and enable providers as described in [Using artificial intelligence](/guides/using-ai). +- The provider is **OpenAI** (not OpenAI-compatible or a gateway). +- The base URL is the default, `https://api.openai.com/v1`. +- The model is one of `gpt-5.5-pro`, `gpt-5.5`, `gpt-5.4`, `gpt-5.4-pro`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini` or `o4-mini`, or a dated snapshot of one of them (such as `gpt-4.1-2025-04-14`). -For self-hosted deployments: +Otherwise, the assistant tells you it can't browse with this model and asks you to paste what it needs, such as the text of a job page. -- app-owned URL crawling is not available; -- web access depends on the selected provider/model supporting native web search; -- unsafe/private AI provider base URLs require `FLAG_ALLOW_UNSAFE_AI_BASE_URL=true`, which should only be used on trusted self-hosted deployments. +## Limits -## Attachments +| Limit | Value | +| --- | --- | +| Edits per proposal set | 12 | +| Tool steps per reply | 30 | +| Length of one reply | About 4 minutes, then "Time limit reached. Your progress is saved. Ask me to continue." | +| Attachments per message | 10 | +| Size per attachment | 25 MB | +| Attachments per conversation | 100 MB in total | +| Text read from one attachment | 40,000 characters | -Attach files from the chat composer when the agent needs extra context, such as: +## Earlier conversations -- a job description PDF; -- a portfolio brief; -- a screenshot; -- a plain text note with constraints. +Before v6, the AI agent changed resumes directly with an `apply_resume_patch` tool. Conversations from that time still open. Those steps show as **Changed the resume directly (an earlier conversation)**, and the assistant no longer has that tool. -Self-hosted deployments need S3-compatible storage for private agent attachments. Local filesystem storage rejects private objects. +The assistant's tools are separate from the MCP server, which lets outside AI clients such as Claude or Cursor work with your resumes. See [Using the MCP server](/guides/using-the-mcp-server). -## Good prompts for tool use +## Related guides -Use direct prompts that tell the agent what context to use and how cautious to be: - -- "Research this role, identify the most important keywords, and apply a conservative patch." -- "Read the attached job description and ask me before changing anything outside the summary." -- "Compare my current projects against this company page and suggest only truthful wording." -- "Apply a patch for the visible resume name, then show me what changed." - -## When tools are unavailable - -Tool use can be limited by the selected provider, deployment configuration, or thread state. - -- A deleted provider makes the thread read-only; a disabled or untested provider blocks new agent runs until it is enabled and tested again. -- A deleted working resume makes the thread read-only. -- Archived threads cannot receive new messages. -- Live web research is unavailable when the selected provider/model does not support native web search. -- Attachments may fail if private object storage is not configured. + + + Ask for changes and review proposed edits. + + + Choose the provider and model the assistant uses. + + diff --git a/docs/guides/arranging-the-layout.mdx b/docs/guides/arranging-the-layout.mdx new file mode 100644 index 000000000..699789c98 --- /dev/null +++ b/docs/guides/arranging-the-layout.mdx @@ -0,0 +1,95 @@ +--- +title: "Arranging the layout" +description: "Move the sidebar left or right, change its width, move sections between columns and pages, split sections into columns, and control page breaks." +--- + +Every resume has a layout: a list of pages, and on each page a **main** column and a **sidebar**. This guide shows how to change the sidebar, move sections between columns and pages, show a section's entries in several columns, and decide where pages break. + +## How the layout works + +- **Pages.** You decide which sections start on which page. If a page's sections don't fit, the rest runs onto an extra page; see [Fitting content on a page](/guides/fitting-content-on-a-page). +- **Main and sidebar.** On a two-column template, the sidebar prints as its own narrow column. On a one-column template, sidebar sections print after the main sections. +- **Print order.** In Write, the list of sections shows them in the order they print: page by page, the main column first and then the sidebar. Dividers mark where each page and each sidebar starts. + + + The Write panel's section list headed Sections, print order, with a Page 1 divider over Summary, Education and Experience, a Sidebar divider over Profiles and Skills, and a Page 2 divider over Experience and Awards + + +## Move the sidebar and change its width + +On a two-column template, a **Sidebar** panel appears under the template gallery in **Design → Template**. + + + The Sidebar panel with a Left and Right switch, Left selected, and a Width slider set to 30% + + +- **Left** / **Right** puts the sidebar on that side, whatever the template's usual side is. +- **Width** sets the sidebar's share of the page, from 26% to 42%. + +For a width outside that range, use **Sidebar Width** in **Advanced → Layout**, which goes from 10% to 50%. + +## Reorder sections and move them between columns + +In **Write**: + +- Drag a section by its handle (the dotted grip on the left) to a new place in the list. Dragging it below a **Sidebar** divider moves it into the sidebar; dragging it past a **Page** divider moves it to that page. +- Or focus a section's title and press ⌥ ↑ or ⌥ ↓ (Alt ↑ or Alt ↓ on Windows and Linux). +- Or open the section's **⋯** menu and select **Move up** or **Move down**. + +For more on the section menu, see [Managing sections](/guides/managing-sections). + +## Manage pages in Advanced → Layout + +**Advanced → Layout** shows every page as a box with its **Main** and **Sidebar** columns. Open **Advanced** at the bottom of the Design panel to find it. + + + The Layout editor showing Page 1 with a Full Width switch and a Delete Page button, a Sidebar column containing Profiles and Skills, and a Main column containing Summary, Education and Experience + + +Here you can: + +- **Drag** sections between columns and between pages. +- Select **Add Page** (below the last page) to add an empty page. +- Select **Delete Page** to remove a page. Its sections move to the first page (or to the second page, if you delete the first). You can't delete the only page. +- Turn on **Full Width** to print that page in a single column. Its sidebar sections move into the main column. + +Each section also has a **⋮** menu: + + + The menu for the Summary section with Move to open, listing Page 1, Page 2, Page 3 and New Page, and below it Keep together with the note Only applies when the section fits on a single page, and Start on new page + + +- **Move to** → a page → **Main** or **Sidebar** moves the section there. **New Page** moves it onto a page of its own at the end. Sidebar isn't offered on full-width pages or with one-column templates. +- **Keep together** and **Start on new page** control page breaks, described below. + +## Show a section's entries in columns + +Short entries, such as skills, languages or interests, can sit side by side. In **Write**, open the section's **⋯** menu, point to **Columns** and choose **1 column** to **6 columns**. + + + The Skills section's menu in Write with Columns open, listing 1 column (checked) through 6 columns and 1 column, inline, and further items Keyword layout, Keep on one page, Start on a new page and Clear section + + +The Skills section has two extra choices: + +- **1 column, inline** runs skills together on one line instead of stacking them. +- **Keyword layout** shows each skill's keywords **Inline** (on one line) or as a **Bulleted list**. + +## Control page breaks + +Two switches decide how a section behaves at the end of a page. You find them in the section's **⋯** menu in Write and in its **⋮** menu in Advanced → Layout; both places change the same setting. + +| In Write | In Advanced → Layout | What it does | +| --- | --- | --- | +| **Keep on one page** | **Keep together** | Moves the whole section to the next page instead of splitting it. It only works when the section fits on one page. | +| **Start on a new page** | **Start on new page** | Always begins the section at the top of a new page. | + + + Keeping a section together can leave a large gap at the bottom of the previous page. If that happens, move a shorter section into the gap or turn the switch off. + + +## Related guides + +- [Choosing a template](/guides/choosing-a-template): one- and two-column templates. +- [Managing sections](/guides/managing-sections): add, rename, hide and remove sections. +- [Fitting content on a page](/guides/fitting-content-on-a-page): keep to the pages you planned. diff --git a/docs/guides/changing-appearance-and-language.mdx b/docs/guides/changing-appearance-and-language.mdx new file mode 100644 index 000000000..a21fe2dec --- /dev/null +++ b/docs/guides/changing-appearance-and-language.mdx @@ -0,0 +1,66 @@ +--- +title: "Changing appearance and language" +description: "Switch Reactive Resume between light, dark and system themes, change the interface language, and see how the app handles reduced motion." +--- + +You can choose how the app looks and which language its menus and buttons use. These settings change the app only. Your resumes always print on white paper, in the language set for each resume. + +## Change the theme + + + + Select your name at the bottom of the sidebar, then **Settings**, then **Preferences**. + + + + - **Light**: a light background all the time. + - **Dark**: a dark background all the time. + - **System**: follows your device's light or dark setting and switches when it does. This is the default. + + + +The change applies at once. Reactive Resume remembers it in this browser, so another device or browser keeps its own choice. + + + The Preferences page with Light, Dark and System theme tiles under Appearance, an English language picker under Language, and a Motion note + + +### Other ways to switch + +- Select your name at the bottom of the sidebar, point to **Theme**, and choose **Light**, **Dark** or **System**. +- Press ⌘ K (Ctrl K on Windows and Linux), choose **Change theme to…**, then pick a theme. + + + The account menu opened from the sidebar with Settings, Language, Theme and Sign out, and a Theme submenu listing Light, Dark and System with System checked + + +## Change the interface language + + + + Go to **Settings → Preferences**. + + + + Open the picker and choose from more than 50 languages. You can type to filter the list. + + + +The page reloads in the new language. Like the theme, the choice is saved in this browser. + +You can also change it from your name in the sidebar (**Language**), or with ⌘ K and **Change language to…**. + +The interface language doesn't translate your resume. Each resume has its own **Language** setting in the **Page** group of the editor's **Design** mode, which sets the section titles and date words on the page. See [Set the language](/guides/selecting-page-format#set-the-language). + + + Spotted a wrong or missing translation? Select **Help translate** under the language picker to suggest a fix on Crowdin. See [Translations](/contributing/translations). + + +## Reduce motion + +Reactive Resume has no motion switch of its own. It follows your operating system's reduced-motion setting: turn that on, and the app replaces movement with simple fades. Nothing in the app animates on its own without you doing something first. + +## Related guides + +- [Using the command bar](/guides/using-the-command-bar): change settings and jump anywhere from the keyboard. +- [Updating your profile](/guides/updating-your-profile): your name, photo, username and email. diff --git a/docs/guides/checking-service-status.mdx b/docs/guides/checking-service-status.mdx index bb4d52924..0df12c142 100644 --- a/docs/guides/checking-service-status.mdx +++ b/docs/guides/checking-service-status.mdx @@ -1,89 +1,66 @@ --- title: "Checking service status" -description: "Check the current uptime and incident status for Reactive Resume's hosted servers, and see what to do when the service is down or slow." +description: "Find out whether rxresu.me is up, see its recent uptime, and learn what to do when Reactive Resume is slow or unreachable." --- -## Status page +If rxresu.me won't load, is slow, or something that normally works keeps failing, check the status page first. It tells +you whether the problem is on our side or yours. -You can check the health of Reactive Resume's servers at any time on the status page: +## Check the status page - - View real-time server metrics including uptime, CPU usage, memory usage, and more. +Open [status.rxresu.me](https://status.rxresu.me). The page shows: + +- Whether the service is **Online** right now, and for how long. +- Uptime for the last 24 hours, the last 7 days and each recent month. +- When it last checked. The service is checked every minute from several locations around the world. + + + Live and recent uptime for rxresu.me. -The status page shows: - -- **Uptime**: How long the servers have been running without interruption -- **CPU Usage**: Current processor utilization -- **Memory Usage**: RAM consumption across services -- **Response Times**: How quickly the servers are responding to requests - ---- - -## What to do if servers are down - -If the servers are under high load or unreachable: +## If the service is down or slow - - Visit [status.rxresu.me](https://status.rxresu.me) to confirm if there's an ongoing issue. The page will show you the current state of all services. + + If the status page shows a problem, it's on our side. You don't need to do anything else. - - - If the servers are under heavy load, wait a while and try again. Peak usage times can cause temporary slowdowns. - - - Reactive Resume is a **free, open-source service** used by thousands of people worldwide. During peak times, the servers may experience higher than usual load. - - + + Most outages and slowdowns are short. Try again in a few minutes. Your documents are safe on the server while it's + unavailable. - - - For major outages or planned maintenance, announcements may be posted on our [GitHub repository](https://github.com/reactive-resume/reactive-resume). + + Longer outages and planned maintenance are announced on + [GitHub](https://github.com/reactive-resume/reactive-resume) and the + [Discord server](https://discord.gg/aSyA5ZSxpb). ---- +## If the status page says everything is fine -## A note on server capacity +The problem is probably between your device and the service. Try these in order: - - Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a - single developer, it's not feasible to invest in powerful dedicated servers without community support. - +1. Reload the page. +2. Check your internet connection by opening another website. +3. Open rxresu.me in a private window. If it works there, a browser extension, such as an ad or script blocker, may be + getting in the way. +4. Try a different browser or device. -Thousands of people use the service every day. Because it is free and has no venture funding behind it, server capacity stays constrained. +If you lose your connection while editing, the editor says so under the document name and keeps your changes on this +device. They're sent as soon as you're back online. Downloading and sharing are unavailable until then. -If Reactive Resume is useful to you and you want to help keep the servers running (and maybe scale them up), consider supporting the project. +Still stuck? Ask in [GitHub Discussions](https://github.com/reactive-resume/reactive-resume/discussions/categories/q-a). +If you can reproduce a bug, [open an issue](https://github.com/reactive-resume/reactive-resume/issues). ---- +## If you run your own copy -## Support the project +The status page only covers rxresu.me. A self-hosted copy reports its own health at `/api/health` on your address, for +example `https://resume.example.com/api/health`. It returns `healthy` when the database, file storage and (if you use it) +Redis are working, and an HTTP `503` error when any of them fails. See [Self-hosting with Docker](/self-hosting/docker) for setup and +health checks. -Donations go directly toward server costs, better infrastructure, and keeping Reactive Resume free for everyone. +## Help keep the service running - - Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the - service running. - - - - - Make a single contribution of any amount to help with immediate server costs. - - - Become a backer with a monthly contribution to provide sustainable support. - - - ---- - -## Self-hosting as an alternative - -If you need guaranteed uptime, or want to avoid the limits of a shared server, you can self-host Reactive Resume on your own infrastructure. - - - Learn how to deploy Reactive Resume on your own servers using Docker. - - -Self-hosting gives you full control over your data and infrastructure, availability that depends only on your own server capacity, and no resources shared with other users. +Reactive Resume is free and has no ads or investors. Server costs are paid by donations, so busy periods can still slow +it down. If Reactive Resume helps you, consider +[donating on Open Collective](https://opencollective.com/reactive-resume/donate). If you need guaranteed availability, +you can [host your own copy](/self-hosting/docker). diff --git a/docs/guides/checking-your-resume.mdx b/docs/guides/checking-your-resume.mdx new file mode 100644 index 000000000..ae0ecd436 --- /dev/null +++ b/docs/guides/checking-your-resume.mdx @@ -0,0 +1,177 @@ +--- +title: "Checking your resume before you apply" +description: "Use Check mode to see how well software reads your resume, fix issues pinned to their lines, match a job posting and review your wording." +--- + +Before you send a resume, open **Check** in the editor. It shows how reliably an applicant tracking system (ATS) can read your resume, pins each problem to the line it's about, and compares your resume with a job posting. Everything except the optional writing review runs in your browser. + + + The resume editor in Check mode, with a score of 86, three numbered issue cards in the panel and a matching numbered pin in the page margin + + +## Open Check + +In the editor bar, select **Check**, or press 3 when you aren't typing in a field. The tab shows how many issues are open, or a check mark when there are none. + +Check updates as you edit. Switch to **Write**, change something, and the score and issues follow straight away. + +## Read the score + +The panel starts with a score from 0 to 100 and a short verdict: **Reads cleanly** (80 and above), **Mostly readable** (50 to 79) or **Hard for software to read** (below 50). + + + Score card reading 86, Reads cleanly, 19 of 22 checks pass, 3 things to review, above the Issues, Job match and Writing tabs and an issue card titled Sidebar is read after the main column + + +The score is the share of checks your resume passes, for example **19 of 22 checks pass**. There are 22 checks, grouped into five categories: + +| Category | What it looks at | +| --- | --- | +| **Contact details** | Your name, email, phone and location are there, email and links are in a form software recognises, and whether a photo is showing. | +| **Dates** | Every experience and education entry has dates, in a form software reads, that don't run backwards or start in the future. | +| **Layout** | Every section with entries is placed on a page, text reads in order (sidebars, multi-column sections), and body text size, line height and page margins aren't too small. | +| **Section headings** | Headings are ones software looks for, such as Experience, Education and Skills, and some work history shows. | +| **Writing** | Every role describes what you did. | + +The heading check only runs when your resume's language is English, so resumes in other languages have 21 checks. + + + The score measures how reliably software can read your resume. It doesn't predict whether you'll be shortlisted. + + +Below the issues, **Checks by category** lists each category with how many checks pass, or how many need a look. Select a row to read what it covers. + + + Checks by category list: Contact details 1 to review, Dates 5 of 5, Layout 2 to review, Section headings 2 of 2, Writing 1 of 1, followed by the button Also check the exported PDF + + +## Fix issues + +The **Issues** tab lists every open issue as a numbered card, most serious first. Each card names its category, says what's wrong in plain words, and offers a fix. + + + + Each issue about a specific part of your resume has a matching numbered pin in the page margin, with a wavy underline under that part. Select a card, or its **Show on page** button, to outline the line on the page and scroll to it. Select a pin on the page to jump to its card. + + + The Volunteer section of a resume outlined in amber, with a numbered pin 2 in the left margin + + + + Many issues fix in one step. The card's main button says what it does, for example **Hide the photo**, **Use one column**, **Switch to one column** or **Use the standard heading**. A message confirms the change and offers **Undo**. + + Other issues need you to write something, such as a missing phone number or a role with no description. Their button, for example **Add a phone number** or **Describe the role**, switches to **Write** and opens the right field. + + + If an issue doesn't apply to you, select **Ignore**. For a choice that is yours to make, such as a two-column design, the button reads **Keep** instead. Ignored issues no longer count against the score. To bring them back, select **Show them again** under the list. + + + +When every check passes, the tab reads **Nothing to fix.** + + + On a phone, selecting a pin opens a bar at the top of the page that reads "Issue 1 of 3". Use the arrows to step through the issues and fix each one from the card at the bottom, without leaving the page. + + +## See what a parser reads + +At the top of the page, switch from **What a person sees** to **What a parser reads**. The page is replaced by the text software pulls out of your resume's PDF, in the order it comes out, with the name, email, phone, location, links, section headings and dates it recognised. + + + The parser view, listing the name, email, phone, location, links and sections software found in the resume's PDF, marked as text layer with 2 columns + + +Use it to spot content that lands in the wrong place, for example a sidebar read after your experience, or a field picked up from the wrong line. Switch back with **What a person sees**. + +## Match your resume to a job + +The **Job match** tab picks out the terms a job posting stresses and shows which ones your resume already has. It isn't part of the score. + + + + If the resume is linked to an application that has a saved posting, Job match uses that posting. If the linked application has no posting yet, paste it into the box and select **Save to the application**. Otherwise, choose an application from the list, or paste the posting into the box and select **Match this posting**. + + A pasted posting is kept for this visit only. To keep it, select **Save as application…**, enter the **Company** and **Role**, and select **Save and link**. This creates an application and links the resume to it. + + + The tab shows how many posting terms appear, for example **19 of 25 posting terms appear**. Terms under **Not in your resume · add only if true** are missing. Terms under **Already covered** are there already: select one to highlight where it appears on the page. + + + Select a missing term to see how often the posting uses it, then choose: + + - **Add to Skills** to add it as a keyword on your first visible skill, which the button names (for example **Add to Skills · Unity Engine**). If you have no skills, it creates one. + - **Ask the assistant to work it in** to open the Assistant, which asks you before it adds anything. + - **Not true for me, hide it** to leave it out of the match. Hidden terms are listed under the tab with **Show again**. + + + + + Job match tab showing a pasted posting, 19 of 25 posting terms appear, missing terms such as Perforce and agile, the options for Perforce, and a list of terms already covered + + +Once a resume is linked, the top of the tab names the application. Select **Change** to link a different application or to unlink it. + + + Job match source card reading Senior Gameplay Engineer at Northwind Games, Posting from the linked application, with a Change button + + +## Check the exported PDF + +The checks above read your resume's content and settings. To test the actual file a recruiter receives, select **Also check the exported PDF** at the bottom of the **Issues** tab. Reactive Resume creates the PDF in your browser and runs the full file check on it, the same one the [ATS checker](/guides/using-the-ats-checker) uses. Nothing is uploaded. + +A message tells you whether the PDF reads cleanly or how many more things there are to look at. Anything it finds is pinned to the page with a PDF icon and a dashed outline. + + + A blue PDF pin in the page margin next to a dashed outline around the resume's profile links + + +Select **Show** in the message, or any PDF pin, to open the full report. It gives a score out of 100, a score for each category (**Readability**, **Layout**, **Sections**, **Contact details**, **Dates**), and each finding with what to do about it. **Writing** holds tips that don't affect the score. + + + Dialog titled The exported PDF, with a score of 96 out of 100, 54 of 56 applicable checks passed, and the Layout category open showing two warnings + + +The pins show while the **Issues** tab is open, until you change the resume. After an edit, run the check again. + +## Get a second opinion on your wording + +The checks are mechanical. The **Writing** tab asks an AI model how a reader might react to your summary and bullets, and suggests rewrites. It needs your own AI provider; see [Connecting an AI provider](/guides/using-ai). Without one, the tab offers **Open AI settings**. Issues and Job match work without AI. + + + + The tab says where your resume's text goes, for example "Sends your resume's text only to OpenAI · gpt-5-mini, with your key." To use another of your providers, select **Change** and pick it. + + + Writing tab titled A second opinion on your wording, stating the resume text is sent only to OpenAI gpt-5-mini, with a Change link, a provider picker and a Review writing button + + + + Select **Review writing**. The review reads up to 120 bullets and paragraphs. + + + Rewrites of your own bullets appear as **Proposed edits**, numbered in the page margin. Select **Accept** or **Reject** on each, or **Accept all**. With an edit focused, A accepts it, R rejects it, and ↑ ↓ move between edits. + + Advice that isn't a rewrite appears as a note marked **High**, **Medium** or **Low**, with **Show on page**. **What's working** lists your strengths. Select **Run again** for a fresh review. + + + + + A model's opinion can be wrong, and the review never changes the score. Accept only the rewrites that are true to your experience. + + +## Related guides + + + + Check any resume PDF, from any tool, without signing in. + + + Copy a resume for an application and adjust it to the posting. + + + Ask the AI assistant to rework parts of your resume. + + + Move sections between columns and pages. + + diff --git a/docs/guides/choosing-a-template.mdx b/docs/guides/choosing-a-template.mdx index c58e544b5..029a8926f 100644 --- a/docs/guides/choosing-a-template.mdx +++ b/docs/guides/choosing-a-template.mdx @@ -1,166 +1,166 @@ --- title: "Choosing a template" -description: "Compare the built-in Reactive Resume templates, preview each layout, and switch between them from the builder to pick the best design for your resume." +description: "Browse the 15 Reactive Resume templates drawn with your own content, preview one on the page, and apply it without losing any of your work." --- -Reactive Resume includes many templates, each with its own design. This guide covers how they differ and how to switch between them. +A template decides the layout of your resume: where the header sits, whether there is a sidebar, and how headings and entries look. You can switch templates at any time. Your content, fonts and colors stay the same. -## How to change your template - -You can change templates at any time without losing your content. +## Open the template gallery - - Navigate to your Dashboard and click on the resume you want to edit. - - - Screenshot of your resumes dashboard showing all your resume - - + + From **Documents**, open the resume you want to change. - - - In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options. - - - - In the right sidebar, find and click on the **Template** section to expand it. - - - Screenshot of the template section in the right sidebar - - + + Select **Design** at the top of the editor, or press 2 while you are not typing in a field. - - - Browse the available templates and click the one you want. Your resume updates immediately. - - - Screenshot of selecting a new template from the gallery - - - - - - After changing the template, review your resume in the live preview. You may want to adjust spacing, colors, or layout to optimize for the new design. - - - Screenshot of the resume with a new template applied - - - - Different templates may display your content differently. Some templates work better with shorter content, while others are designed to handle more detailed information. - - + + **Template** is the first group in the Design panel. Use the row of links at the top of the panel (**Template**, **Type**, **Color**, **Page**, **Advanced**) to jump between groups. -## Tips for choosing the right template + + The Template group in the Design panel, showing the All, One column, Two columns and ATS-safe filters, a count of 15 of 15 shown, and thumbnails of the Azurill, Bronzor, Chikorita and Ditgar templates with Azurill checked + - - - Different industries have different expectations: - - **Creative fields** (design, marketing, arts): Templates with more visual flair like Gengar or Pikachu - - **Corporate/Traditional** (finance, law, consulting): Clean, minimal templates like Onyx or Ditto - - **Tech/Startups**: Modern, balanced templates like Chikorita or Leafish - +Each thumbnail shows the first page of **your** resume in that template, so you can judge it with your real content. While the thumbnails are being drawn, you briefly see a sample image instead. - - If you have a lot of experience to fit, choose a template that uses space efficiently. For shorter resumes, - templates with more white space can make your content feel more substantial. - +## Filter the gallery - - Your resume is part of your personal brand. Choose a template that reflects your personality while remaining - professional and appropriate for your target roles. - +The chips above the thumbnails narrow the list: - - Don't choose a template based on how it looks empty. Fill in your actual content and see how it flows across pages. What looks great with sample data might not work as well with your specific information. - - +| Filter | Shows | +| --- | --- | +| **All** | All 15 templates. | +| **One column** | Templates that read top to bottom in a single column. | +| **Two columns** | Templates with a separate sidebar column. | +| **ATS-safe** | Templates with one reading order and plain text headings, which applicant tracking systems read cleanly. | ---- +The counter on the right (for example "7 of 15 shown") tells you how many templates match. -## Available templates +## Preview and apply a template -Each template can be customized further with your choice of colors, fonts, and layout options. + + + Hover over a thumbnail, or move to it with Tab. The page on the right redraws in that template, and a label above it reads "Previewing *name* · click to apply". Nothing is saved yet. + + + Click the thumbnail. The template is applied and a message confirms the change, with an **Undo** button if you change your mind. + + + Move the pointer away from the thumbnails, or press Esc, to return to your current template. + + - - All templates support the same features and sections. They differ only in how they present your information. - + + The Design panel with the pointer over the Chikorita thumbnail, and the page on the right showing the resume in the Chikorita template with a dark label reading Previewing Chikorita, click to apply + + + + On a phone or tablet, press and hold a thumbnail to preview it, then tap it to apply it. On phones, Design opens as a sheet over the lower half of the page and the templates scroll sideways in a strip. + + +## The 15 templates + +All templates support the same sections and features. They differ only in how they arrange your information. The images below use the same resume in each template. + +| Template | Columns | ATS-safe | Sidebar side | +| --- | --- | --- | --- | +| Azurill | Two | No | Left | +| Bronzor | One | Yes | — | +| Chikorita | Two | No | Right | +| Ditgar | Two | No | Left | +| Ditto | Two | No | Left | +| Gengar | Two | No | Left | +| Glalie | Two | No | Left | +| Kakuna | One | Yes | — | +| Lapras | One | Yes | — | +| Leafish | Two | No | Right | +| Meowth | One | Yes | — | +| Onyx | One | Yes | — | +| Pikachu | Two | No | Left | +| Rhyhorn | One | Yes | — | +| Scizor | One | Yes | — | + +In a one-column template, sections you placed in the sidebar print after the main sections instead of beside them. Bronzor prints them as labelled rows. On two-column templates you can move the sidebar to the other side; see [Arranging the layout](/guides/arranging-the-layout).
- Azurill template preview + First page of a resume in the Azurill template: centred photo and header, sidebar on the left + + + First page of a resume in the Bronzor template: one column with labelled section rows + + + First page of a resume in the Chikorita template: header over the main column and a colored sidebar on the right + + + First page of a resume in the Ditgar template: header inside a colored left sidebar + + + First page of a resume in the Ditto template: colored header band across the page, sidebar on the left + + + First page of a resume in the Gengar template: header in a colored left sidebar with a tinted summary band + + + First page of a resume in the Glalie template: header and contact details in a light left sidebar + + + First page of a resume in the Kakuna template: centred header, one column + + + First page of a resume in the Lapras template: one column with sections in outlined boxes + + + First page of a resume in the Leafish template: tinted header across the page, sidebar on the right + + + First page of a resume in the Meowth template: one column with position, organization and period on one line + + + First page of a resume in the Onyx template: one column with the photo beside the header + + + First page of a resume in the Pikachu template: colored header block over the main column, sidebar on the left - - - Bronzor template preview - - - - Chikorita template preview - - - - Ditto template preview - - - - Ditgar template preview - - - - Gengar template preview - - - - Glalie template preview - - - - Kakuna template preview - - - - Lapras template preview - - - - Leafish template preview - - - - Meowth template preview - - - - Onyx template preview - - - - Pikachu template preview - - - Rhyhorn template preview + First page of a resume in the Rhyhorn template: minimal top header and one column - - Scizor template preview + First page of a resume in the Scizor template: one column with uppercase section headings
---- +## Tips for choosing -## Customizing your template +- **Applying through job portals?** Start with the **ATS-safe** filter. Two-column layouts can be read out of order by applicant tracking systems, and Check mode warns you when sidebar sections might be affected. See [Checking your resume](/guides/checking-your-resume). +- **Lots of experience?** Compare how full each thumbnail's first page looks. After you apply a template, the page count in the zoom bar at the bottom of the page tells you the total. +- **Judge with real content.** Because the gallery draws your own resume, fill in your details first and then pick. -After selecting a template, you can adjust: +## Reset the design settings -| Setting | Description | -| -------------- | ------------------------------------------------------------ | -| **Colors** | Change the primary color scheme to match your personal brand | -| **Typography** | Choose from various Google Fonts for headings and body text | -| **Layout** | Adjust sidebar width, section order, and page margins | -| **Spacing** | Fine-tune gaps between sections and elements | +If you have changed a lot of design settings and want a clean slate, open **Advanced** at the bottom of the Design panel and select **Reset to template defaults**. This resets fonts, text size, line height, colors, level style, sidebar width, margins, spacing and the icon and underline switches. Your template, paper size, language, date format, section placement and custom CSS stay as they are. A message offers **Undo**. + +Despite the name, the reset doesn't depend on the template: every template goes back to the same standard settings (IBM Plex Serif at 10 pt, a red accent, circle levels and hidden section icons). Pick a font pairing and accent color again afterwards if you want a different starting point. + + + If Design's controls are greyed out, the resume is locked. Open the document name menu at the top left and select **Unlock editing**. + + +## Related guides + + + + Pick a font pairing, text size and density. + + + Set the accent color and check it reads well. + + + Move the sidebar, change its width and split sections across pages. + + + Keep your resume to the number of pages you planned. + + diff --git a/docs/guides/choosing-colors.mdx b/docs/guides/choosing-colors.mdx new file mode 100644 index 000000000..c64289dc3 --- /dev/null +++ b/docs/guides/choosing-colors.mdx @@ -0,0 +1,78 @@ +--- +title: "Choosing colors" +description: "Pick an accent color for your resume headings and icons, check its contrast, and change the text, background and skill level styles." +--- + +Your resume uses one accent color for headings, icons and colored bands such as a template's header or sidebar. Body text stays near-black so it prints well and reads cleanly in applicant tracking systems. This guide shows how to choose the accent, check that it's readable, and adjust the other colors and the level indicators. + +## Pick an accent color + + + + In the editor, select **Design** (or press 2), then select **Color** in the row of links at the top of the panel. + + + Select one of the eight swatches: Moss, Ink blue, Teal, Plum, Rust, Burgundy, Ochre or Graphite. Hover over a swatch to see its name. All eight are dark enough to read on white. + + + + + The Color group with eight round swatches, Ink blue selected, a hex field reading #2F5A8A with a contrast ratio of 7.1:1, and a note that the accent is used for headings, icons and the header band + + +## Use your own color + +Type a six-digit hex code, such as `#1F6E73`, in the field under the swatches. The `#` is optional. The page updates as soon as the code is complete, and the square on the left shows the color. + +Next to the field is the color's **contrast ratio** against white. Headings need at least 4.5:1 to read comfortably. Below that, the ratio turns amber and a warning appears: + + + The Color group with a custom color #0084D1 at 4.0:1, and an amber warning reading Too light for headings on white. It may be hard to read and print faintly, with a Use a darker shade button + + +Select **Use a darker shade** to keep the same hue but darken it until it reads well on white (at least 4.6:1). + +## Change text and background colors + +The Color group only sets the accent. For the other colors, open **Advanced** at the bottom of the Design panel and find its **Design** section: + +- The row of 22 swatches sets the accent (primary) color. These include bright colors that may not pass the contrast check. +- **Primary Color** is the accent. +- **Text Color** is the color of body text. +- **Background Color** is the page color. + +Each has a color picker (the circle) and a text field that shows the value in the form `rgba(47, 90, 138, 1)`. Use the picker, or type a new value in the same form. + + + The Advanced Design section showing 22 square color swatches, the Primary Color, Text Color and Background Color fields with rgba values, and the Level area with a preview of three out of five stars, an Icon picker set to a star and Type set to Icon + + + + Colored or dark backgrounds, and light text, can be hard to read when printed and may confuse some applicant tracking systems. The contrast check in the Color group doesn't look at these fields. + + +## Change how skill levels look + +Skills and languages can show a level, for example 4 out of 5. Under **Level** in **Advanced → Design**, choose how every level on your resume is drawn. The preview above the controls shows level 3 in your accent color. + +| Type | Looks like | +| --- | --- | +| **Hidden** | No level is shown. | +| **Circle** | Filled and empty dots. | +| **Square** | Filled and empty squares. | +| **Rectangle** | Short bars. | +| **Rectangle (Full Width)** | Bars that stretch across the column. | +| **Progress Bar** | One continuous bar. | +| **Icon** | A repeated icon, such as a star. Choose it with the **Icon** button. | + +You set each entry's level while writing it; see [Editing entries](/guides/editing-entries). + + + Cover letters have the same Color controls in their own Design mode. See [Writing a cover letter](/guides/writing-a-cover-letter). + + +## Related guides + +- [Customizing typography](/guides/customizing-typography): fonts, size and density. +- [Choosing a template](/guides/choosing-a-template): templates decide where the accent appears. +- [Checking your resume](/guides/checking-your-resume): find readability issues before you apply. diff --git a/docs/guides/creating-an-account.mdx b/docs/guides/creating-an-account.mdx index d445e1e59..d5a682f53 100644 --- a/docs/guides/creating-an-account.mdx +++ b/docs/guides/creating-an-account.mdx @@ -1,88 +1,76 @@ --- title: "Creating an account" -description: "Sign up for a Reactive Resume account with email and password or social sign-in from Google or GitHub so you can start building and saving resumes." +description: "Sign up for Reactive Resume with an email and password, a passkey, or a social account, and choose the username that appears in your public links." --- +You need a free account to save resumes, cover letters and job applications. Signing up takes under a minute, and you can start building right after. + +## Sign up with email and password + - - Head over to [https://rxresu.me](https://rxresu.me) and click on the Get Started button. + + Go to [rxresu.me](https://rxresu.me) and select **Build your resume**. On the sign-in page, select **Create one now**. + + If you run your own copy of Reactive Resume, use its address instead of `rxresu.me`. - - - You should see a link that says Don't have an account? Create one now →. Click on that link to go to the sign up page and you should see a form. - - + - Complete the sign up form with the following information: - - - **Name**: Your full name - - **Email Address**: A valid email address you have access to. - - **Username**: Choose a unique username (this will be used in your public resume URLs) - - **Password**: Create a strong password + - **Name**: your name as you want it shown in the app (3 to 64 characters). + - **Username**: 3 to 64 characters, using lowercase letters, numbers, dots, hyphens and underscores. It becomes part of every public resume link, for example `rxresu.me/alexmorgan/game-developer`. + - **Email Address**: an address you can open. You need it to reset a forgotten password. + - **Password**: at least 8 characters, up to 64. + - - Make sure to choose a username you're happy with, as it will be part of your public resume URL (e.g., `rxresu.me/your-username/resume-slug`). - + + Your account is created and you are signed in straight away. + - - - - After filling in all the required fields, click the **Sign up** button. You will be signed in immediately and can start using Reactive Resume right away. - - - No email verification is required to get started, but verifying your email is strongly recommended for account security. - - - - - Email verification is optional, but strongly recommended. Verifying your email: - - - confirms you have access to the address on the account - - lets you reset your password if you forget it - - - You can verify your email at any time from your account settings. Look for the verification prompt in your dashboard or navigate to **Settings → Account**. - - - - - Click on **Continue** and you should be taken to your Dashboard, where you can: - - - Create your first resume - - Import an existing resume - - Manage your account settings + + A **You've got mail!** screen confirms that a verification link is on its way. Verifying is optional, so you can select **Continue** and start working, then open the link from your inbox later. ---- + + The Create a new account form with Name, Username, Email Address and Password fields, a Sign up button, and Passkey, Google, GitHub and LinkedIn buttons below + -## Account security tips + + The You've got mail! screen saying to check your email for a verification link, with a note that the step is optional and a Continue button + - - - Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters. - - - Setup two-factor authentication or passkeys on your account to add an extra layer of security. - - + + You can change your name, username and email later in **Settings → Account**. Changing your username changes every public link you have shared, so pick one you are happy to keep. + ---- +## Sign up with a social account -## Troubleshooting +Under **or continue with**, select **Google**, **GitHub** or **LinkedIn** and approve the request on that site. Reactive Resume creates your account from the name, email and photo the provider shares, and picks a username for you. You can change it afterwards in **Settings → Account**. + +These buttons only appear when the site has set them up. On a self-hosted copy you may see none of them, or a single button for your organization's sign-in service. + +## If sign-up doesn't work - - If your username is already taken, try a different variation. Usernames must be unique across all users. - + + Usernames and email addresses are unique. Try another username. If the email is yours, you already have an account: go back to the sign-in page and use **Forgot Password?** to get back in. + - - If your email is already registered, you can use the **Forgot Password** link on the login page to reset your - password and regain access to your account. - - - - Check your spam folder first. If it isn't there, request a new verification email from your account settings. - + + Check your spam folder. You can ask for a new link from **Settings → Account**: under the **Email** field, select **Not verified yet. Resend the link**. On self-hosted copies without email delivery, the field says so instead, and no email is sent. + + + The site's owner has turned off new sign-ups. Only existing accounts can sign in. + + +## Next steps + + + + Start from a blank page, a sample or an existing file. + + + Add two-step verification or a passkey. + + diff --git a/docs/guides/creating-your-first-resume.mdx b/docs/guides/creating-your-first-resume.mdx index 3c4d51ca8..5889843ae 100644 --- a/docs/guides/creating-your-first-resume.mdx +++ b/docs/guides/creating-your-first-resume.mdx @@ -1,85 +1,107 @@ --- title: "Creating your first resume" -description: "Create your first resume in Reactive Resume by naming it, choosing a URL slug, and opening it in the builder to start filling in your details." +description: "Start a blank resume in Reactive Resume, add your name and contact details, fill in your first section, and download it as a PDF." --- +This guide takes you from an empty account to a first resume you can download. It takes a few minutes. If you already have a resume as a PDF, Word or JSON file, you can [import it](/guides/importing-resumes) instead of starting blank. + +## Before you start + +You need a Reactive Resume account. If you don't have one yet, see [Creating an account](/guides/creating-an-account). Sign in at [rxresu.me](https://rxresu.me), or at your own address if you host Reactive Resume yourself. + +## Start a blank resume + - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. - - - If you haven't created an account yet, follow the guide on [Creating an account](/guides/creating-an-account). - + + After you sign in, you land on **Documents**, the page that lists all your resumes and cover letters. On your first visit it's empty and offers three ways to begin. + + The empty Documents page with the heading Let's start with what you have, a Choose a file button, and Start blank and Try a sample links + - - After signing in, go to your Dashboard, where all of your resumes live. + + Select **Start blank**. You can also select **New** in the sidebar (or press N) and choose **Start blank** in the **New document** dialog. - - This is where you can create, import, organize, and manage multiple resumes for different roles or versions. - + + The New document dialog with Import a resume, Copy a resume for a job, Start blank, New cover letter instead and Try with a sample resume + - - - - In the Resumes Dashboard, click on the Create a new resume card to open the creation form. - - - Create a new resume dialog with name, slug, and tags fields - - - - - Fill in the resume name. This can be generic (e.g., "General Resume") or tied to the position you're applying for. - - - If you can't think of a name yet, click the magic wand button to generate one. - - - - - - The Slug field is auto-filled based on the name, but you can change it to anything you like. - - - If you choose to publicly share your resume, it will be accessible at `https://rxresu.me/{username}/{slug}`. - - - - - - Add any Tags you want. Think of tags like folders or labels to help organize many resumes. - - - You can filter resumes by tags later from the Dashboard. The tag filter appears after you have at least one tag. - - - - - - Click **Create** to start with an empty resume, or open the split-button menu and choose **Create a Sample Resume** to start with sample content. - - - A sample resume is useful when you want to try templates, layout controls, and exports before entering your own - information. - - - - - - Once created, a new card for your resume will appear on the Dashboard. Click it to open the resume builder and start editing. + Reactive Resume creates the resume straight away and opens it in the editor. There's no name or address to fill in first. -## What you can do next + + Want to look around before typing anything? Choose **Try a sample** (or **Try with a sample resume** in the dialog) to get a filled-in resume you can explore and delete later. + -After creating your first resume, you can: +## Add your name and contact details -- change the template in [Choosing a template](/guides/choosing-a-template); -- adjust the page format in [Selecting the right page format](/guides/selecting-page-format); -- download a copy in [Exporting your resume](/guides/exporting-your-resume); -- organize, duplicate, lock, or delete resumes in [Managing resumes from the dashboard](/guides/managing-resumes-from-the-dashboard). +The editor opens in **Write** mode with the cursor in **Full name**. The page on the right updates as you type, and every change saves on its own. The editor bar shows **Saved** under the document name. + + + + Type your **Full name**, then a **Headline** (for example, the job title you're aiming for), your **Email**, **Phone** and **Location**. Add a **Website** if you have one. + + + + A new blank resume is called "Untitled resume". Until you name it yourself, it takes its name from your **Headline**, so it's easy to find later. To pick your own name, open the document name menu at the top left and choose **Rename…**, or rename it from Documents. + + + + + The resume editor in Write mode with name, headline, email, phone and location filled in on the left and the same details on the page preview on the right + + +## Add your first section + +Below your details, under **Sections · print order**, Reactive Resume suggests the sections most resumes have. + + + The Sections list on a new resume with buttons for Experience, Education, Skills and Summary, an Import it link, and an Add section button + + + + + Select **Experience** (or any other suggestion). For anything else, select **Add section**. + + + + The section opens with an empty entry. Fill in **Position** and **Company**, then **Location** and **Dates**. Turn on **Present** if you still work there. An experience entry appears on the page once it has a company. + + + + Select **Add experience** below the entry for your next job, or add more sections the same way. + + + + + An Experience entry for Gameplay Programmer at Northwind Studios in the editor, with the same entry showing on the page preview + + +## Download your resume + +When you're happy with it, select **Download PDF** in the top right corner of the editor. The arrow next to it opens other formats. + + + The right side of the editor bar with the History and Assistant icons, the Share button and the Download PDF button + + +To come back later, select the arrow at the top left of the editor to return to Documents, where your resume now has its own card. + +## Next steps + + + + Write, Design and Check modes, and what each part of the screen does. + + + Photo, extra contact fields and a summary. + + + Change how your resume looks without retyping anything. + + + Rename, duplicate, lock and organize your resumes. + + diff --git a/docs/guides/customizing-typography.mdx b/docs/guides/customizing-typography.mdx new file mode 100644 index 000000000..0212b3437 --- /dev/null +++ b/docs/guides/customizing-typography.mdx @@ -0,0 +1,101 @@ +--- +title: "Customizing typography" +description: "Choose a font pairing, text size and density for your resume, or pick any of about 500 fonts with exact sizes, weights and line height." +--- + +The **Type** group in Design controls how your text looks: the fonts, the text size and how tightly lines and sections are spaced. Five ready-made font pairings cover most resumes. If you need a specific font, **Advanced** lets you choose from about 500. + +## Pick a font pairing + + + + In the editor, select **Design** (or press 2), then select **Type** in the row of links at the top of the panel. + + + Select one of the five pairings. The page updates straight away. Each pairing shows the fonts it uses next to its name. + + + + + The Type group showing five font pairings (Classic, Traditional, Balanced, Professional, Clean) with Balanced selected, a Custom row, the Text size slider at 10 pt with the hint 10 to 11 recommended, and Density set to Normal + + +| Pairing | Heading font | Body font | +| --- | --- | --- | +| **Classic** | EB Garamond | EB Garamond | +| **Traditional** | Tinos | Tinos | +| **Balanced** | Source Serif 4 | Source Sans 3 | +| **Professional** | Carlito | Carlito | +| **Clean** | Lato | Lato | + +A pairing sets body text in Regular and Bold, and headings in a single bolder weight. Tinos and Carlito match the widths of Times New Roman and Calibri, so they suit employers who expect a traditional look. + +If your resume uses fonts that aren't one of the pairings, no pairing is selected and the **Custom** row shows your current fonts instead. + +## Set the text size + +Drag the **Text size** slider. It goes from 9 pt to 12.5 pt in half-point steps, and the current value shows on the right. Headings grow and shrink with it, keeping the same proportion to the body text. The panel suggests 10–11 pt, which reads comfortably on screen and on paper. + + + Dragging a slider counts as one change, so a single ⌘ Z (Ctrl Z on Windows and Linux) undoes the whole drag. + + +## Choose a density + +**Density** sets the line height of your body text and the vertical spacing in and between entries: + +| Density | Line height | Vertical spacing | +| --- | --- | --- | +| **Compact** | 1.35 | 4 pt | +| **Normal** | 1.5 | 6 pt | +| **Roomy** | 1.65 | 8 pt | + +If you set an exact body line height or **Spacing (Vertical)** in Advanced that doesn't match one of these, no density is selected. + +## Use any font + + + + Select the **Custom** row under the pairings. Design opens **Advanced** and moves you to its **Typography** editor, with the body font picker ready. + + + Open **Font Family** under **Body** and type part of a font's name to search. Each font is shown in its own typeface. Choose one to apply it. + + + Do the same under **Heading**. + + + + + The Typography editor with the body Font Family picker open, the search box containing Merri, and the results Merriweather and Merriweather Sans each drawn in its own font + + +The list contains Google Fonts plus the PDF standard fonts Helvetica, Courier and Times-Roman. Fonts for Chinese, Japanese, Korean, Arabic, Hebrew and Thai text are included, and missing characters fall back to a matching Noto font so they still print. + +### Exact typography settings + +**Advanced → Typography** has the same fields for **Body** and **Heading**, plus one **Hyphenation** switch for both: + + + The Typography editor's Body fields: Font Family set to Source Sans 3, Font Weights set to 400, 700, and Font Size set to 10 pt + + +| Field | What it does | +| --- | --- | +| **Font Family** | The font, from the full list. Choosing a new family also picks two of its weights, usually 400 and 600. Adjust them in the weights field if you want real bold (700). | +| **Font Weights** (body) / **Font Weight** (heading) | Which weights to use, from those the font offers. Body text can use several (for example 400 for regular and 700 for bold). | +| **Font Size** | Any size from 6 to 24 pt, in steps of 0.1 pt. | +| **Line Height** | A multiple of the font size, from 0.5 to 4. | +| **Hyphenation** (one switch for all text) | Breaks long words between syllables, using the language set in **Page**. Useful for languages with long compound words, such as German. | + +The **Text size** slider in Type only goes from 9 to 12.5 pt. If you type a size outside that range in Advanced, the slider stops at its nearest end but your exact size is kept. + + + Cover letters have the same Type controls in their own Design mode, without the Custom row. See [Writing a cover letter](/guides/writing-a-cover-letter). + + +## Related guides + +- [Choosing colors](/guides/choosing-colors): set the accent color that headings use. +- [Fitting content on a page](/guides/fitting-content-on-a-page): use size and density to fit your pages. +- [Applying custom styles](/applying-custom-styles): change styles that Design doesn't cover, with CSS. diff --git a/docs/guides/deleting-your-account.mdx b/docs/guides/deleting-your-account.mdx index a69d635f4..79e7e3520 100644 --- a/docs/guides/deleting-your-account.mdx +++ b/docs/guides/deleting-your-account.mdx @@ -1,59 +1,51 @@ --- title: "Deleting your account" -description: "Export a full archive of your Reactive Resume data from the Danger Zone, then permanently delete your account, resumes, and settings." +description: "Permanently delete your Reactive Resume account, including every resume, cover letter, job application, API key and public link. Export a copy first." --- -## Export all of your data +Deleting your account removes it and everything in it for good. There is no grace period and no way to restore it, so download a copy of your data first if you might want it later. -Before you delete your account, you can download a copy of everything Reactive Resume stores for you. +## Before you start - - - In the dashboard sidebar, under **Settings**, click **Danger Zone**. - - - - Reactive Resume gathers your account profile, resumes, and settings into a single archive and downloads it to your - browser. - - - -Keep this archive somewhere safe. It's the easiest way to restore your resumes if you delete your account and later change your mind. +[Export your data](/guides/exporting-your-data) from **Settings → Account → Your data**. The zip holds your resumes, cover letters and job applications as JSON files. ## Delete your account - - Deleting your account is permanent. Export a copy of your data first if you might need it later. - - - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. + + Select your name at the bottom of the sidebar, then **Settings**. On the **Account** page, scroll to **Your data**. - - In the dashboard sidebar on the left, under Settings, click the Danger Zone link. - - - - On the Danger Zone page, type delete into the confirmation input. - - - The Delete Account button will stay disabled until the confirmation text matches exactly. - - + + Next to **Delete account**, select **Delete…**. The **Delete your account?** dialog lists how many documents, applications and API keys will go. - - Click Delete Account, then confirm the final prompt. - - - This action cannot be undone. All your data will be permanently deleted. - - + + Type `delete` into the box. The **Delete** button stays unavailable until the word matches. To back out, select **Keep account**. - - After deletion completes, you will be signed out and redirected to the homepage. + + Reactive Resume deletes your account, signs you out and takes you to the home page. + + + The Delete your account? dialog saying 1 document, 0 applications, 0 API keys and every public link are removed permanently, with delete typed in the confirmation box and Keep account and Delete buttons + + +## What gets deleted + +- Every resume and cover letter, including those in the trash, and their version history. +- Every job application. +- Every public resume link. Anyone who opens one sees a page that no longer exists. +- Your uploaded files, such as photos. +- Your assistant conversations, API keys, saved AI providers, passkeys and linked social accounts. + + + This can't be undone from the app. To use Reactive Resume again, [create a new account](/guides/creating-an-account) and [import](/guides/importing-resumes) resumes you saved as JSON or PDF. + + +## Related guides + +- [Exporting your data](/guides/exporting-your-data): download everything before you delete. +- [Exporting your resume](/guides/exporting-your-resume): keep a PDF or JSON of a single resume. diff --git a/docs/guides/editing-entries.mdx b/docs/guides/editing-entries.mdx new file mode 100644 index 000000000..d68f14e23 --- /dev/null +++ b/docs/guides/editing-entries.mdx @@ -0,0 +1,165 @@ +--- +title: "Editing entries" +description: "Add, reorder, duplicate, hide, move and delete the entries in your resume sections, and learn the fields each kind of entry has, including job roles." +--- + +An entry is one item inside a section: a single job, degree, skill or award. This guide shows how to add and change entries in **Write** mode, move them between sections and pages, and use the fields that only some entries have, such as skill levels and job roles. + +## Before you start + +Open a resume in **Write** mode and open the section you want to work in by selecting its title in the **Sections · print order** list. If the section isn't there yet, add it first: see [Managing sections](/guides/managing-sections#add-a-section). + +## Open and edit an entry + +A closed entry shows its title and a line of details, such as the company, location and dates. Select it to open its fields. Only one entry is open at a time, and the open entry is outlined on the page with an **Editing** tag. You can also select any block on the page to open the matching entry. + +Everything you type saves as you go. There is no save button. + + + The Description field is a rich text editor with bold, lists, links and more. See [Formatting text](/guides/formatting-text). + + +## Add an entry + + + + At the bottom of an open section, select the add button named after the section, such as **Add experience** or **Add skill**. You can also open the section's **⋯** menu and select **Add entry**. + + + The new entry opens at the end of the section with its first field ready for typing. It's marked **Draft** until its main field has text, and a note tells you which field that is, for example "Appears on the page once it has a company." + + + + + An open Experience entry titled Untitled with a Draft badge and the note Appears on the page once it has a company, followed by empty Position, Company, Location, Dates, Link and Description fields and an Add role button. + + +The main field for each kind of entry is listed in [Fields by section type](#fields-by-section-type). Drafts are kept, but they don't print until that field is filled. + +## Reorder entries + +Entries print in the order they're listed. To move one: + +- Hover over the entry and **drag** it by the handle on its left edge. +- Or select the entry's title and press ⌥ Option + ↑ or ↓ (Alt + ↑ or ↓ on Windows and Linux). + +For Experience and Education, **Sort by date** in the section's **⋯** menu orders all entries at once, newest first. + +## The entry menu + +Each entry has its own **⋯** menu on the right of its title. + + + An Experience entry's menu showing Hide from page, Duplicate, Move to… and Delete. The Move to… submenu lists Page 1, Page 2, Page 3 and New page, and Page 2 is open, offering the Earlier Experience section and New section. + + +### Hide an entry from the page + +Select **Hide from page**. The entry gets a **Hidden** badge and stops printing, but keeps all its content. Select **Show on page** in the same menu to bring it back. This is useful for keeping an older job on file without showing it on every version of your resume. + +### Duplicate an entry + +Select **Duplicate**. An exact copy appears right below the original and opens, so you can change the details. Use it when two entries share most of their text. + +### Move an entry to another section or page + +Select **Move to…**, then a page. For each page you see: + +- The sections on that page that hold the same kind of entry. An Experience entry can only move to another Experience-type section, including custom ones. +- **New section**, which creates a new custom section of the same type at the end of that page, named after the section the entry came from. + +At the bottom, **New page** adds a page to the resume with a new section holding the entry. + +When you move the last entry out of a custom section, that empty section is removed. If this leaves a page empty (other than the first page), the page is removed too. + + + Moving older jobs into a second section on page 2 is a tidy way to split a long work history. Rename the new section, for example to "Earlier Experience", from its **⋯** menu. + + +### Delete an entry + +Select **Delete** in the entry menu, or the trash icon that appears next to an open entry's title. The entry is removed right away, and the **Entry deleted** message has an **Undo** button if you change your mind. + +## Fields by section type + +The **main field** is the one an entry needs before it prints. + +| Section type | Fields | Main field | +| --- | --- | --- | +| Experience | Position, Company, Location, Dates, Link, Description, roles | Company | +| Education | School, Degree, Area of study, Grade, Location, Dates, Link, Description | School | +| Projects | Name, Dates, Link, Description | Name | +| Skills | Name, Proficiency, Keywords; in **More options**: Level, icon and Icon colour | Name | +| Languages | Language, Fluency; in **More options**: Level | Language | +| Interests | Name, Keywords; in **More options**: icon and Icon colour | Name | +| Awards | Title, Awarder, Date, Link, Description | Title | +| Certifications | Title, Issuer, Date, Link, Description | Title | +| Publications | Title, Publisher, Date, Link, Description | Title | +| Volunteer | Organization, Location, Dates, Link, Description | Organization | +| References | Name, Position, Phone, Link, Description | Name | +| Profiles | Network, Username, Link; in **More options**: icon and Icon colour | Network | +| Summary (custom sections only) | Text | None | + +Awards, Certifications and Publications have a single **Date**; the others have a start and end. See [Entering dates](/guides/entering-dates). + +### Links + +Most entries have a **Link** field for a web address; you can type it with or without `https://`. The tag icon in the field adds a label, so the page shows text such as "Portfolio" instead of the full address. Tick **Show link in title** to turn the entry's title into the link instead of printing the link on its own line. + +### Keywords + +Skills and Interests take a list of keywords, such as the tools behind a skill. Type a keyword and press Enter or , to add it. Use the pencil on a keyword to edit it, or the × to remove it. To print keywords as a bulleted list instead of a line, use **Keyword layout** in the Skills section's **⋯** menu. + +### Level, icon and colour + +Select **More options** below a skill, language, interest or profile to see the less common fields. + + + An open Skills entry named Unity Engine with Proficiency Expert, three keywords, and More options expanded to show a Level slider at 5 / 5, an icon button and an Icon colour button. + + +- **Level** is a slider from 0 to 5 that prints as dots, bars or icons next to the skill or language. At 0 it reads **Hidden** and no level prints. The shape of the level marker is chosen in **Design** → **Advanced**. +- The **icon** button picks an icon to print before the entry, and **Icon colour** sets its colour. Leave the colour empty to use the template's. Entry icons print only while **Icons in contact line** is on in **Design** → **Page**. + +## Show career progression with roles + +If you held several positions at the same company, add them as roles under one Experience entry instead of repeating the company. + + + + Open the entry for the company. + + + Select **Add role** at the bottom of the entry. Each role has its own **Position**, **Dates** and **Description**. Add one role per position you held. + + + Use the up and down arrows on each role to change the order, usually the most recent first. The trash icon removes a role. + + + + + Role 1, Senior Game Developer from March 2024 to Present, and Role 2, Game Developer from March 2022 to February 2024, each with Position, Dates and Description fields and move and delete buttons, followed by an Add role button. + + +The page prints the company and the entry's own details first, then each role with its dates: + + + The Experience section on the page: Cascade Studios, Seattle, WA, March 2022 to Present, followed by Senior Game Developer, March 2024 to Present, and Game Developer, March 2022 to February 2024. + + +Some things to know about roles: + +- Once an entry has at least one role, the entry's own **Description** field is replaced by the role descriptions. Text you wrote there earlier is kept but doesn't print; remove all roles to see it again. +- The entry's own **Position** and **Dates** still print above the roles. Leave the entry's Position empty and use its Dates for the whole time at the company, as in the example above. +- A role without a Position doesn't print. + +## On a phone + +On a phone, an open entry fills the screen. The back button at the top is named after the section and takes you back to the list, and the trash icon deletes the entry. Everything else works the same way. + +## Related guides + +- [Managing sections](/guides/managing-sections): add, hide, rename and reorder whole sections. +- [Entering dates](/guides/entering-dates): start and end dates, year-only dates and the date format. +- [Formatting text](/guides/formatting-text): bold, lists and links in descriptions. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): go back to an earlier version. diff --git a/docs/guides/editing-on-mobile.mdx b/docs/guides/editing-on-mobile.mdx new file mode 100644 index 000000000..dc1ca7a4c --- /dev/null +++ b/docs/guides/editing-on-mobile.mdx @@ -0,0 +1,123 @@ +--- +title: "Editing on a phone or tablet" +description: "How the Reactive Resume editor works on phones and tablets: the Write, Page, Design and Check tabs, full-screen entries, the design sheet and the panel drawer." +--- + +You can build and edit a resume entirely from a phone or tablet. The editor has the same features as on a computer, arranged to fit a smaller screen. This page explains what changes and where to find things. + +## On a phone + +On a phone (any screen narrower than 640 pixels), the editor shows one thing at a time: either the page or a panel. Tabs at the bottom switch between them. + + + The editor on a phone showing the Basics card, with Write, Page, Design and Check tabs along the bottom + + +| Tab | What it shows | +| --- | --- | +| **Write** | Your details, summary, sections and entries. | +| **Page** | The resume as it will print. | +| **Design** | The page, with design settings in a sheet over its lower half. | +| **Check** | Issues found in your resume, job match and writing review. | + +### The editor bar on a phone + +The bar at the top keeps the back arrow, the document name (which opens the document menu) and the save status. On the right are three icons: + +- **Assistant** opens the AI assistant full screen. +- **Share** opens the Share sheet. +- **Download PDF** downloads the PDF straight away. For other formats, open **Share** and use its **Download** tab. + +There's no **History** button on a phone. Open **Share** and use the **History** tab instead. + +### Edit an entry from the page + + + + Scroll to the part of the resume you want to change. + + + The block is outlined with an **Editing** tag, and an **Edit entry** button appears. + + + The editor switches to **Write** and opens that entry. + + + + + The Page view on a phone with an experience entry outlined and tagged Editing, and an Edit entry button floating above the zoom bar + + +Tap an empty part of the page to dismiss the button. The zoom bar works as it does on a computer, and your zoom level stays the same when you switch tabs. + +### Entries open full screen + +When you open an entry in **Write**, it slides in and fills the screen, with larger fields that are easier to tap. The top of the screen names the section, for example **Experience**. Tap it to go back to the list. The bin icon (**Delete entry**) deletes the entry, and a message offers **Undo**. + + + A full-screen experience entry on a phone with a back button labelled Experience, a delete icon, and Position, Company, Location, Dates, Link and Description fields + + +When you type in a description or the summary, the formatting toolbar docks just above the keyboard. See [Formatting text](/guides/formatting-text#on-a-phone). + +### Change the design + +The **Design** tab keeps the page visible in the top half of the screen, so you can see each change as you make it. The settings sit in a sheet with four tabs: **Template**, **Type**, **Color** and **Page**. The **Advanced** settings are at the bottom of the **Page** tab. + +To see more of the settings, tap the handle at the top of the sheet (**Raise the design sheet**). Tap it again to lower it. + + + The Design view on a phone with the resume page above and a sheet below showing Template, Type, Color and Page tabs and a strip of template thumbnails + + +### Check your resume + +The **Check** tab lists the issues found in your resume. Select **Show on page** on an issue to jump to the **Page** tab: a bar at the top reads, for example, "Issue 1 of 3" with **Previous issue** and **Next issue** buttons, and a card at the bottom explains the issue and offers a fix. See [Checking your resume](/guides/checking-your-resume). + +## On a tablet + +On a tablet, the page fills the screen and the panel opens as a drawer over it. This layout applies to screens from 640 to 1023 pixels wide, which covers most tablets held upright. A tablet held sideways that is 1024 pixels or wider gets the same layout as a computer. You switch modes with **Write**, **Design** and **Check** in the editor bar, as on a computer. + + + The editor on a tablet in landscape with the Write panel open as a drawer on the left and the resume page behind it + + +- **Show or hide the panel.** Select the panel button next to the back arrow (**Show panel** / **Hide panel**). +- **Tap a line on the page** to open the drawer on that entry. Tap an empty part of the page to close the drawer again; your selection stays. +- **Keep the panel beside the page.** Held sideways (landscape), a second button appears next to the panel button: **Keep the panel beside the page**. Select it to pin the panel next to the page instead of over it. Select it again to go back to the drawer. +- The assistant opens as a drawer too. **Download PDF** shows only its icon, and **History** is in the Share sheet's **History** tab. + +## Install Reactive Resume as an app + +You can add Reactive Resume to your home screen so it opens in a window of its own, without the browser's address bar. + +- **iPhone and iPad (Safari):** open Reactive Resume, select the **Share** button in Safari, then **Add to Home Screen**. +- **Android (Chrome):** open Reactive Resume, open Chrome's menu, then select **Add to Home screen** or **Install app**. +- **Computer (Chrome or Edge):** select the install icon at the right of the address bar, then **Install**. + +The installed app is the same website: you sign in the same way, and your documents stay in your account, not on the device. + +## Related guides + +- [Getting to know the resume editor](/guides/editor-overview): the editor on a computer, saving and locking. +- [Editing entries](/guides/editing-entries): adding, moving and deleting entries. +- [Keyboard shortcuts](/guides/keyboard-shortcuts): faster editing with a keyboard. diff --git a/docs/guides/editor-overview.mdx b/docs/guides/editor-overview.mdx new file mode 100644 index 000000000..c0a56eb3b --- /dev/null +++ b/docs/guides/editor-overview.mdx @@ -0,0 +1,174 @@ +--- +title: "Getting to know the resume editor" +description: "A tour of the Reactive Resume editor: the editor bar, the Write, Design and Check modes, the page canvas, autosave, and locking a resume." +--- + +The editor is where you write and shape a resume. This page explains what each part of it does, how your work is saved, and how to lock a finished resume so it doesn't change by accident. + + + The resume editor showing the editor bar, the Write panel with the Basics card and section outline, and the rendered resume page + + +The editor has three parts: + +- **The editor bar** across the top, with the document name, the mode switcher and actions such as **Share** and **Download PDF**. +- **The panel** on the left. What it shows depends on the mode you're in. +- **The page canvas** on the right. It shows your resume exactly as it will print and updates as you type. + +To leave the editor, select the back arrow (**Back to documents**) at the far left of the bar. + +## The editor bar + + + The editor bar with the back arrow, the document name and Saved status, the Write, Design and Check switcher, and the History, Assistant, Share and Download PDF buttons + + +From left to right: + +| Part | What it does | +| --- | --- | +| Back arrow | Returns to your Documents. Any unsaved changes are saved first. | +| Document name | Opens the document menu (see below). Under the name is the save status. | +| **Write** · **Design** · **Check** | Switches the panel between modes. **Check** shows how many issues are open, or a check mark when there are none. | +| **History** (clock icon) | Opens the **History** tab of the Share sheet, where you can see and restore earlier versions. | +| **Assistant** (sparkle icon) | Opens or closes the AI assistant. | +| **Share** | Opens the Share sheet on its **Link** tab. When the resume is public, the button also shows a **Public** badge. | +| **Download PDF** | Downloads the resume as a PDF straight away. The arrow next to it (**More download formats**) opens the **Download** tab with every other format. | + +On narrower screens some buttons show only their icon, and **History** moves into the Share sheet. See [Editing on a phone or tablet](/guides/editing-on-mobile). + +### The document menu + +Select the document name to open the document menu. + + + The document menu open under the resume name, listing Rename, Duplicate, Lock editing, Notes, Details, Print and Move to Trash + + +| Item | What it does | +| --- | --- | +| **Rename…** | Changes the resume's name and tags. | +| **Duplicate** | Makes a copy of the resume. | +| **Lock editing** / **Unlock editing** | Makes the resume read-only, or editable again. See [Locking a resume](#locking-a-resume). | +| **Notes** | Opens your private notes for this resume. See [Using private notes](/guides/using-private-notes). | +| **Details** | Shows when the resume was created and last edited, its template, language, length in pages and whether it's shared. | +| **Print** | Prepares the PDF and opens your browser's print dialog. | +| **Move to Trash** | Moves the resume to Trash and takes you back to Documents. A message offers **Undo**, and the resume stays in Trash for 30 days. See [Using the trash](/guides/using-the-trash). | + +## The three modes + +The mode switcher in the middle of the bar changes what the panel shows. The page stays in view in every mode. + +- **Write** is for content: your name and contact details, the summary, and every section and entry. See [Filling in your details](/guides/filling-in-your-details) and [Managing sections](/guides/managing-sections). +- **Design** is for the look of the page: template, fonts, colors, page format and layout. See [Choosing a template](/guides/choosing-a-template). +- **Check** reviews the resume for problems, and can compare it with a job posting. See [Checking your resume](/guides/checking-your-resume). + +When you're not typing in a field, press 1, 2 or 3 to switch to Write, Design or Check. The mode is part of the page address, so reloading the page or opening a bookmark keeps you in the same mode. + +## The page canvas + +The page on the right is a live preview of the PDF you'll download. Each page has a label above it, such as **Page 1**. + +- **Click a line on the page to edit it.** Hovering over the page tints the block under the pointer. Clicking it selects that entry, marks it with an **Editing** tag, and opens it in the Write panel. Clicking your name or contact details opens the Basics card. Press Esc to clear the selection. +- **Proposed edits** from the assistant or from Check appear on the page, with old text struck through and new text highlighted. Nothing changes until you accept them. +- **Overflow warning.** If your content runs onto an extra page, the label above the first page says so, for example "Runs onto page 2 by about 6 lines", and offers **Fit to one page**. See [Fitting content on a page](/guides/fitting-content-on-a-page). + +### Zooming + +A small zoom bar floats at the bottom of the canvas. + + + The zoom bar with a minus button, the Fit button, a plus button and the page count + + +- Select **−** or **+** to zoom out or in, in 10% steps from 60% to 150%. +- Select the middle button to fit the page to the width of the canvas. It reads **Fit** when the page is fitted, and the zoom level otherwise. You can also press ⌘ 0 (Ctrl 0 on Windows and Linux). +- The number on the right is how many pages the resume has. + +Zoom only changes what you see in the editor. It has no effect on the PDF or any other download. + +## How your work is saved + +Reactive Resume saves as you type. There is no save button. Changes are sent about half a second after you stop typing, and the status under the document name tells you where things stand: + +| Status | Meaning | +| --- | --- | +| **Saved** | Everything you see is saved to your account. | +| **Saving…** | Your latest change is on its way. | +| **Offline · saved on this device** | You've lost your connection. Your changes are kept in this browser and sent when you're back online. | +| **Not saved · Retry** | The server couldn't save your changes. They're kept in this browser. Select **Retry** to try again. | + + + The save status reading Offline, saved on this device, above a notice that says you can keep editing and that Download and Share need a connection + + +A few things to know: + +- **You can keep editing offline.** A notice at the top of the panel explains that changes sync when you're back online. **Share** and **Download PDF** are unavailable until then. +- **Unsaved changes survive a reload or a closed tab.** If you come back to a resume whose last changes never reached the server, the editor restores them and says "Restored changes that hadn't been saved yet." +- **Leaving the editor waits for the save.** If saving takes more than about 10 seconds, you stay in the editor and see a message that your changes are still open. +- Pressing ⌘ S (Ctrl S) only reminds you that changes are saved automatically. + +Each visit in which you make changes also leaves an autosave version in History, so you can go back to how the resume looked before. Within a visit, press ⌘ Z (Ctrl Z) while you're not typing in a field to undo your last change, and ⇧ ⌘ Z (Ctrl Y) to redo it. There's no undo button in the editor bar. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). + +### Editing in more than one place + +If the same resume changes somewhere else while you have it open (in another browser tab, on another device, or through an AI agent or app connected to your account), the editor picks up the change on its own and shows a short message: "Synced changes made in another tab." or "This resume was updated by an AI agent." + +- If you're typing in a field at that moment, the update waits until you leave the field, so your typing isn't interrupted. +- If you have changes of your own that haven't been saved yet, your changes win. +- After an outside change, ⌘ Z can't undo past it. Use History to go further back. + +## Locking a resume + +Lock a resume when it's finished, or when you've sent it for an application and want to keep it exactly as it was. You can still view, download, print and duplicate a locked resume, and a public link you already made keeps working, but the content and design can't change. + + + + Select the document name in the editor bar. + + + The resume locks straight away. There's no confirmation, because unlocking is one click. A lock icon appears next to the document name. + + + +While a resume is locked: + +- The Write panel shows **Locked. Unlock to edit.** and every field is read-only. +- The Design panel is read-only. +- **Rename…** and **Move to Trash** are unavailable in the document menu. +- Share link settings can't be changed, and History can't restore an older version over it. + + + The document name with a lock icon, and a notice in the panel reading Locked. Unlock to edit, with an Unlock button + + +To edit it again, select **Unlock** in the notice, or choose **Unlock editing** from the document menu. + +You can also lock and unlock resumes from the Documents page. See [Managing documents](/guides/managing-documents). + +## Related guides + +- [Filling in your details](/guides/filling-in-your-details): your name, contact details, photo and summary. +- [Formatting text](/guides/formatting-text): bold, lists, links and the formatting toolbar. +- [Editing on a phone or tablet](/guides/editing-on-mobile): how the editor changes on smaller screens. +- [Keyboard shortcuts](/guides/keyboard-shortcuts): every shortcut in one list. +- [Exporting your resume](/guides/exporting-your-resume): PDF, Word, Markdown and JSON. diff --git a/docs/guides/entering-dates.mdx b/docs/guides/entering-dates.mdx new file mode 100644 index 000000000..05f844824 --- /dev/null +++ b/docs/guides/entering-dates.mdx @@ -0,0 +1,97 @@ +--- +title: "Entering dates" +description: "Type start and end dates, year-only dates and current roles in Reactive Resume, fix dates that need a look, and choose how every date prints." +--- + +Dates in Reactive Resume are structured: you type a month and year (or only a year), and the resume prints every date in one consistent format that you choose. This keeps your resume tidy, lets **Sort by date** work, and helps applicant tracking systems read your timeline. + +## Enter dates for an entry + +Experience, Education, Projects and Volunteer entries have a start and an end date. Each job role has its own pair too. + + + + In **Write** mode, open the entry you want to date. See [Editing entries](/guides/editing-entries) if you're not sure how. + + + In the first box under **Dates**, type the date the way you normally write it, for example `Mar 2022`, `March 2022`, `03/2022`, `2022-03`, or only `2022`. A date that can be read saves as you type. When you leave the box, it switches to your resume's date format. + + + Type the end date in the second box. If you're still in the role or still studying, turn on **Present** instead. The end box then shows the word "Present" and can't be edited. + + + + + The Dates field with Mar 2022 in the start box, the end box greyed out showing Present, and the Present switch turned on. + + +Awards, Certifications and Publications have a single **Date** box and no Present switch. + +## Year-only dates + +If you don't remember the month, or prefer not to show it, type only the year, such as `2014`. Year-only dates are exact, not guesses: they print as the year alone whatever date format you choose, for example "2014 – 2018". + +## How dates print + +The page shows the start and end joined by a dash. If there's only a start date, only that date prints; with **Present** on, the end reads "Present". + + + The Education section showing University of Washington with 2014 – 2018, and the Experience section showing Cascade Studios with March 2022 – Present and two roles with their own dates. + + +Month names and the word "Present" follow the resume's language, which you set in **Design** → **Page** → **Language**. For example, a resume in German prints "März 2022 – Heute". You can also type month names in the resume's language. + +## Choose the date format + + + + Select **Design** in the editor bar, then open **Advanced** at the bottom of the panel. + + + Choose one of the four formats in **Date format**. Every date on the resume changes at once. + + + + + The Advanced group in Design mode, with a Date format dropdown set to March 2022. + + +| Option | Prints as | +| --- | --- | +| **Mar 2022** | Short month name and year (the default) | +| **March 2022** | Full month name and year | +| **03/2022** | Month number and year | +| **2022-03** | Year and month number (ISO) | + +Resumes brought over from an earlier version of Reactive Resume start with whichever of these formats is closest to how their dates were typed. **Reset to template defaults** in the same panel doesn't change your date format. + +## Fix dates that need a look + +The Dates field tells you when something is off. + +- **"Use a month and year, like Mar 2022, or just a year."** The text you typed can't be read. The entry keeps the last date that could be read until you correct it. +- **"The end is before the start."** The two dates are the wrong way round. Swap them, or the entry sorts to the end when you use **Sort by date**. + + + The Dates field with Spring typed in the start box and 2024 in the end box, and a red message below: Use a month and year, like Mar 2022, or just a year. + + +### Dates from imports and older resumes + +When you import a resume, or open one made before dates were structured, Reactive Resume converts each date from its text. Dates it can't read exactly, such as "Summer 2016", are flagged: + +- The section's row in the list shows a **to check** badge with the number of entries to look at. +- The entry's Dates field explains what happened, for example: We read "Summer 2016". Pick a month so it sorts and prints consistently. +- Until you edit the dates, the entry prints the original text as it was written. + +Type the dates, or change any part of them, and the note clears. + + + **Check** mode also flags entries without dates, dates that can't be read, dates that run backwards and dates in the future. See [Checking your resume](/guides/checking-your-resume). + + +## Related guides + +- [Editing entries](/guides/editing-entries): the other fields in each entry, including job roles. +- [Managing sections](/guides/managing-sections): sort Experience and Education by date from the section menu. +- [Importing resumes](/guides/importing-resumes): bring in an existing resume and review what was read. diff --git a/docs/guides/exporting-resume-to-markdown.mdx b/docs/guides/exporting-resume-to-markdown.mdx index f217ccc47..059f4e26c 100644 --- a/docs/guides/exporting-resume-to-markdown.mdx +++ b/docs/guides/exporting-resume-to-markdown.mdx @@ -1,104 +1,99 @@ --- title: "Exporting a resume to Markdown" -description: "Export your Reactive Resume as a portable Markdown file for AI tools, personal websites, notes, version control, or quick plain-text edits." +description: "Download your resume as a Markdown file: plain text with headings for application forms, AI tools, notes, websites and version control." --- -Use Markdown when you want the content of your resume in a portable plain-text format. Markdown keeps headings, lists, links, bold text, and italic text, but it does not try to preserve the visual template, page layout, colors, or spacing from the PDF. +Markdown is plain text with a few symbols for headings, lists and links. A Markdown export gives you the words of your resume without its design, which is what you want when you paste into a form, feed an AI tool or keep your resume in a notes app. -Use PDF when you are submitting the finished resume. Use Markdown when you want to reuse, review, transform, or store the resume content. +For applying to jobs, download a PDF instead. See [Downloading your resume](/guides/exporting-your-resume). -## Export Markdown +## Download the Markdown file - - Go to the dashboard and open the resume you want to export. + + In the editor, select the arrow beside **Download PDF**, or press ⌘ ⇧ E (Ctrl Shift E on Windows and Linux). - - - Click **Download** in the builder header, or open the **Export** section in the right sidebar and click **Download**. + + Select **Markdown**. The file name ends in `.md`; change the name in **File name** if you like. - - - In the **Markdown** row, click **Download**. Your browser saves a `.md` file. + + Select **Download Markdown**. Your browser saves the file. - - Download dialog showing PDF, DOCX, Markdown, and JSON export options for a resume + + Download tab of the Share and export sheet with Markdown selected, the file name David-Kowalski-Resume.md and a Download Markdown button -## Use cases +## What the file looks like -Markdown export is useful when you need readable resume content outside the PDF. - -| Use case | Why Markdown helps | -| --- | --- | -| **AI agents and assistants** | Markdown gives the model structured text with headings and lists, without the layout noise of a PDF. | -| **File search and retrieval systems** | `.md` files are easy to index, search, chunk, and cite in retrieval workflows. | -| **GitHub profile or portfolio repositories** | GitHub renders Markdown files, so you can reuse resume content in a profile README or portfolio repo. | -| **Personal websites and static sites** | Many site generators and CMS workflows accept Markdown as source content. | -| **Notes and knowledge bases** | Apps like Obsidian use Markdown syntax, so your resume can live next to job-search notes and interview prep. | -| **Version control** | Markdown diffs cleanly, making it easier to review what changed between resume versions. | -| **Quick editing** | Open the file in any text editor, make content edits, then copy the text wherever you need it. | - -## Example Markdown - -A shortened export of the built-in sample resume looks like this: +This is the start of a Markdown export of the sample resume, shortened: ```md # David Kowalski _Game Developer | Unity & Unreal Engine Specialist_ -david.kowalski@email.com - +1 (555) 291-4756 - Seattle, WA - [davidkowalski.games](https://davidkowalski.games) +david.kowalski@email.com · +1 (555) 291-4756 · Seattle, WA · [davidkowalski.games](https://davidkowalski.games) · [github.com/dkowalski-dev](https://github.com/dkowalski-dev) · [itch.io/dkowalski](https://itch.io/dkowalski) ## Summary -**Passionate game developer with 5+ years of professional experience** creating engaging gameplay systems and polished player experiences across multiple platforms. Specialized in Unity and Unreal Engine with strong expertise in C#, C++, and game design principles. - -## Experience - -### Cascade Studios - Senior Game Developer (March 2022 - Present) - -_Seattle, WA_ - -- Lead gameplay programmer on an unannounced AAA action-adventure title built in Unreal Engine 5 for PC and next-gen consoles -- Architected and implemented core combat system including hit detection, combo mechanics, and enemy AI behavior trees serving 15+ enemy types -- Developed custom editor tools in C++ that reduced level designer iteration time by 40% and improved workflow efficiency across the team -- Optimized rendering pipeline and gameplay systems to maintain 60 FPS performance target on all supported platforms +**Passionate game developer with 5+ years of professional experience** creating engaging gameplay systems and polished player experiences across multiple platforms. ## Education -### University of Washington - Bachelor of Science, Computer Science (2014 - 2018) +### University of Washington — Bachelor of Science, Computer Science (2014 – 2018) -_Seattle, WA - Grade: 3.6 GPA_ +_Seattle, WA · Grade: 3.6 GPA_ -Concentration in Game Development. Relevant Coursework: Game Engine Architecture, Computer Graphics, Artificial Intelligence, Physics Simulation, 3D Mathematics, Software Engineering, Data Structures & Algorithms +## Experience -## Skills +### Cascade Studios — Senior Game Developer (March 2022 – Present) -### Unity Engine - Expert +_Seattle, WA_ -C# - Editor Tools - Performance Profiling +- Lead gameplay programmer on an unannounced AAA action-adventure title built in Unreal Engine 5 +- Developed custom editor tools in C++ that reduced level designer iteration time by 40% -### Unreal Engine - Advanced +## Profiles -C++ - Blueprints - UE5 Features +- **GitHub** — dkowalski-dev ([github.com/dkowalski-dev](https://github.com/dkowalski-dev)) +- **LinkedIn** — davidkowalski ([linkedin.com/in/davidkowalski](https://linkedin.com/in/davidkowalski)) ``` -## What Markdown includes +Your name becomes the top heading and each section a second-level heading. Entries such as jobs, schools and projects get a third-level heading with their dates; shorter entries such as skills, languages and profiles become one bullet each. Sections follow your pages in order, the main column first and then the sidebar. -Markdown export includes the visible resume content. It keeps the document structure, section headings, item headings, rich-text paragraphs, lists, links, bold text, and italic text. +## What's included -Markdown export does not include visual-only choices such as template design, page size, column layout, colors, icon styling, spacing, or PDF page breaks. +The file keeps: + +- your name, headline and contact details; +- every visible section and entry, with section titles in your resume's language; +- paragraphs, lists, links, and **bold** and _italic_ text. Numbered lists become bullet points. + +It leaves out: + +- hidden sections and entries; +- your photo; +- the template, fonts, colors, columns, icons and page breaks. + +## Ways to use it + +| Use | Why Markdown helps | +| --- | --- | +| **Application forms** | Many job portals have plain text boxes. Markdown pastes cleanly, without the stray characters a PDF copy often brings. | +| **AI tools** | Headings and lists give an AI tool clear structure, without the layout noise of a PDF. Paste the job description alongside it and ask for suggestions. | +| **Notes apps** | Apps such as Obsidian use Markdown, so your resume can sit next to your interview notes. | +| **Websites and GitHub** | Many site builders take Markdown, and GitHub displays `.md` files, for example in a profile README. | +| **Version control** | Plain text shows clearly what changed from one version to the next. | - If you are sharing the file with an AI assistant, include the job description in the same prompt and ask for changes as Markdown. That makes the result easy to compare before you update the resume in Reactive Resume. + Reactive Resume has its own AI assistant that can edit your resume directly. See [Using the assistant](/guides/using-the-assistant). -## When not to use Markdown +If an AI tool rewrites your Markdown, review the changes, then copy the parts you want back into the editor. Reactive Resume doesn't import Markdown files. -Do not use Markdown as the final file for most job applications. Applicant portals and recruiters usually expect a PDF or DOCX file. Export Markdown when you need a working copy of the content, then export PDF when you are ready to submit. +## Related guides + +- [Downloading your resume](/guides/exporting-your-resume): PDF, Word and JSON, and when to use each. +- [Sharing your resume with a link](/guides/sharing-your-resume-publicly): send a link instead of a file. diff --git a/docs/guides/exporting-your-data.mdx b/docs/guides/exporting-your-data.mdx new file mode 100644 index 000000000..44704c89f --- /dev/null +++ b/docs/guides/exporting-your-data.mdx @@ -0,0 +1,45 @@ +--- +title: "Exporting your data" +description: "Download everything in your Reactive Resume account, including resumes, cover letters and job applications, as JSON files in a single zip archive." +--- + +You can download a copy of everything you have stored in Reactive Resume in one step. Use it as a backup, to keep a record before [deleting your account](/guides/deleting-your-account), or to take your data elsewhere. + +## Download the archive + + + + Select your name at the bottom of the sidebar, then **Settings**. On the **Account** page, scroll to **Your data**. + + + + Next to **Export everything**, select **Export**. Your browser downloads a file named `reactive-resume-` followed by today's date, for example `reactive-resume-2026-09-30.zip`. + + + + + The Your data section with an Export everything row and Export button, and a Delete account row with a Delete button + + +## What's in the zip + +| File | Contents | +| --- | --- | +| `account.json` | Your name, email, username, photo address, whether your email is verified, and when the account was created and last updated. | +| `applications.json` | Every job application with its status, job details, notes, contacts, follow-ups and activity. | +| `resumes/` | One JSON file per resume, including those in the trash. Each holds the resume's name, public address, tags, sharing settings and the full resume content. | +| `letters/` | One JSON file per cover letter. | + +Each resume and letter file is named after the document plus a unique ID, so two documents with the same name don't overwrite each other. + +The archive doesn't include your password, passkeys, two-step verification codes, API keys or AI provider keys. Uploaded images, such as resume photos, are listed by their address rather than included as files. + + + To move one resume into another account or another Reactive Resume site, download it as **JSON** from the **Share** sheet's **Download** tab and import that file. The resume files in this archive wrap the content with extra details, so they aren't meant for **Import**. See [Exporting your resume](/guides/exporting-your-resume). + + +## Related guides + +- [Exporting your resume](/guides/exporting-your-resume): PDF, Word, Markdown and JSON for a single resume. +- [Importing resumes](/guides/importing-resumes): bring a resume file into Reactive Resume. +- [Deleting your account](/guides/deleting-your-account): remove your account and everything in it. diff --git a/docs/guides/exporting-your-resume.mdx b/docs/guides/exporting-your-resume.mdx index ae8973420..9effca475 100644 --- a/docs/guides/exporting-your-resume.mdx +++ b/docs/guides/exporting-your-resume.mdx @@ -1,173 +1,104 @@ --- -title: "Exporting your resume" -description: "Download your resume from Reactive Resume as PDF, DOCX, Markdown, or JSON, and pick the right format for each situation." +title: "Downloading your resume" +description: "Download your resume as a PDF in one click, or as Word, Markdown or JSON from the Share sheet. Learn which format to use and how to print." --- -Reactive Resume can export your resume in four formats. +When a job application asks for a file, download your resume from the editor. A PDF takes one click. Word, Markdown and JSON files are one step further, in the **Share & export** sheet. -| Format | Best for | +## Choose a format + +| Format | Use it for | | --- | --- | -| **PDF** | Job applications, recruiter emails, printing, and public resume downloads. | -| **DOCX** | Further editing in Microsoft Word, Google Docs, or Pages. | -| **Markdown** | Plain-text edits and sharing structured content with AI tools. | -| **JSON** | Backups, restoring a resume later, or importing into another Reactive Resume account. | +| **PDF** (`.pdf`) | Applying for jobs and emailing recruiters. It looks exactly like the page in the editor. | +| **Word** (`.docx`) | Job portals or recruiters who ask for a Word document. The layout is simplified, so it won't match the PDF exactly. | +| **Markdown** (`.md`) | Plain text with headings, for pasting into application forms, notes or AI tools. See [Exporting a resume to Markdown](/guides/exporting-resume-to-markdown). | +| **JSON** (`.json`) | A complete backup of the resume that you can import back into Reactive Resume later. | -## Open the download dialog +If you're unsure, choose PDF. It's the format most employers expect. -Every export runs from the same **Download** dialog. You can open it two ways. +## Download a PDF + +In the editor bar, select **Download PDF**, or press ⌘ P (Ctrl P on Windows and Linux). The button shows **Preparing…** while the file is made, then your browser saves it. + + + Green Download PDF button with a separate arrow segment on its right + + +The file is named after the full name on your resume, such as `David-Kowalski-Resume.pdf`. If the resume has no name yet, the document's name is used. + + + In the editor, ⌘ P downloads the PDF instead of opening your browser's print dialog. To print, use **Print** in the document menu (see [Print your resume](#print-your-resume)). + + +## Download another format - - Go to the dashboard and open the resume you want to export. - + + Select the arrow beside **Download PDF** (**More download formats**), or press ⌘ ⇧ E (Ctrl Shift E). The **Share & export** sheet opens on its **Download** tab. - - Select the **Download** button in the top-right of the builder header, next to the sidebar toggle. You can also open the same dialog from the **Export** section of the right sidebar. + On a phone, select **Share** in the editor bar, then the **Download** tab. - - - Select **Download** on the format you want. The dialog closes and your browser saves the file using the resume name as the filename. + + Select **PDF**, **Word**, **Markdown** or **JSON**. Each option says what it's best for. + + + The **File name** field shows the name recruiters will see. Change it if you like; the extension is added for you. Characters that computers don't allow in file names, such as `/` or `:`, are removed as you type. + + + Select the download button at the bottom, for example **Download Word**. It shows its progress, and a message confirms the file name once it's saved. - - While an export is running, the trigger button and format buttons show a spinner and stay disabled until the file is ready. - + + Download tab listing PDF (marked Best for applying), Word, Markdown and JSON, a File name field with David-Kowalski-Resume, a note that Check has 3 things to review, and a Download PDF button + -## Cover letters +A few things you may see on this tab: -Cover letters are documents of their own. Export one from the letter itself: open it and click **Share & export**. See [Adding a cover letter](/guides/adding-a-cover-letter). When the resume's application has a letter, the **Download** tab offers to include it as a second file. +- **Check has _n_ things to review. You can still download.** Check mode found issues on your resume. They never block a download; select **Review** to see them first. +- **Also download the _company_ cover letter** appears when this resume belongs to a job application that has a cover letter. Tick it to get both files at once, in the same format. +- If a Word, Markdown or JSON file can't be made, the tab says so. Select **Try again**, or **Download PDF instead**. -## Export as PDF +## What each file contains -Choose **PDF** when you need a file that preserves your resume layout. - -Use PDF for: - -- uploading to job applications; -- emailing recruiters or hiring managers; -- printing from your browser or a print service; -- sharing a finished copy outside Reactive Resume. - - - The PDF is generated from your current resume data, template, typography, design, layout, and page settings. - - -## Export as DOCX - -Choose **DOCX** when you want to continue editing your resume in a word processor. - -Use DOCX for: - -- making final manual edits in Microsoft Word; -- collaborating with someone who prefers Word or Google Docs; -- submitting to a system that requires a Word document. +- **PDF** keeps your template, fonts, colors, layout and page breaks. Hidden sections and entries are left out. +- **Word** keeps your content, headings and lists in an editable document. Visual details such as columns, colors and icons are simplified, your photo isn't included, and hidden content is left out. +- **Markdown** keeps headings, lists, links, bold and italic text. It leaves out hidden content and all visual design. +- **JSON** keeps everything: every section and entry, hidden ones included, your design settings and your private notes. To restore it, select **New**, then **Import a resume**, and choose the file. Importing creates a new resume; it doesn't overwrite one. - DOCX exports are useful for editing, but the visual layout may not match the PDF exactly. Use PDF when exact visual fidelity matters. + A JSON file includes your private notes and hidden entries. Treat it as a backup for yourself, not a file to send to employers. -## Export as Markdown +## Print your resume -Choose **Markdown** when you want a plain-text version of your resume that is easy to edit anywhere and easy to feed into AI tools. + + + In the editor bar, select the resume's name. + + + Select **Print**. Reactive Resume prepares the same PDF you'd download and opens your browser's print dialog. If your browser blocks that, the PDF opens in a new tab so you can print it from there. + + -Use Markdown for: + + Document menu under the name Game Developer Resume, with Rename, Duplicate, Lock editing, Notes, Details, Print and Move to Trash + -- pasting into an AI assistant to review, rewrite, or tailor content; -- quick edits in any text editor without formatting overhead; -- storing a lightweight, diff-friendly copy in version control. +Printing uses the PDF, so the printout matches the file recruiters receive. -Section headings, lists, links, and rich-text formatting from the builder are preserved as standard Markdown. +## Downloads by visitors -For examples and common workflows, see [Exporting a resume to Markdown](/guides/exporting-resume-to-markdown). +If your resume has a public link, visitors can download the PDF from the public page, unless you turn off **Visitors can download the PDF**. These downloads show up in your statistics. See [Sharing your resume with a link](/guides/sharing-your-resume-publicly). -## Export as JSON + + **Download PDF**, **Share** and their shortcuts are unavailable while you're offline. They come back when your connection does. + -Choose **JSON** when you want a structured backup of your resume. +## Related guides -The JSON export includes your full resume content and settings. Cover letters are exported on their own. You can import it later from the dashboard to restore the resume or create another version. - -JSON is also useful when working with AI assistants that understand structured data. Export JSON, ask an assistant to review or edit the fields, then import the revised JSON as a new resume. - - - JSON export in the download dialog is only available on the **Resume** tab. To export an independent letter as JSON, open it from **Cover Letters** on the dashboard and select **Export JSON**. - - - - Review AI-generated changes before importing or using them. AI assistants can make mistakes or add details that do not reflect your experience. - - -## Keep JSON backups in a local Git repository - -Git can keep owner-controlled versions of your exported resume and cover letter without changing Reactive Resume or connecting it to a Git provider. This workflow is manual and local only: Reactive Resume does not commit, synchronize, or upload the files for you. - -### Choose the right exports - -Reactive Resume provides three different JSON shapes: - -| Export | How to create it | What it contains | Can you import it as one resume? | -| --- | --- | --- | --- | -| **Single resume** | In the builder, open **Download** and download **JSON**. | Resume content and settings. | Yes. | -| **Independent cover letter** | Open a letter from **Cover Letters** on the dashboard and select **Export JSON**. | That letter's content and copied resume styling in the Reactive Resume cover-letter format. | No. Import it through the cover-letter library instead. | -| **Account archive** | Open **Settings**, go to **Account**, and under **Your data** select **Export**. | A zip with `account.json` (profile and an `exportedAt` timestamp), each resume and cover letter as its own JSON file, and `applications.json`. | No. It is an archive, not a single-resume import file. | - -Use the single-resume and independent-cover-letter exports for the Git workflow below. An account archive is an additional portability backup, not a replacement for those files. Because its `exportedAt` timestamp changes on every export, do not expect otherwise unchanged account archives to be byte-for-byte identical. - -### Create the local backup - -1. Create a folder outside your Reactive Resume source checkout. -2. Export the resume and each independent cover letter you want to keep. -3. Give the files stable, descriptive names. The example below uses `resume.json` and `cover-letter.json`; replace each file with its latest export instead of changing its name. -4. From that folder, initialize Git and inspect exactly what you are about to commit: - -```bash -git init -git add -- resume.json cover-letter.json -git diff --cached --stat -git diff --cached -- resume.json cover-letter.json -git commit -m "Back up resume and cover letter" -``` - -After the first commit, replace the JSON files with fresh exports and inspect the visible changes before committing again: - -```bash -git diff -- resume.json -``` - -Stable filenames and the exports' two-space indentation make field changes easier to review. Repeat the add, diff, and commit steps only after the changes look correct. - - - Resume JSON, cover-letter JSON, and especially an account archive can contain private data such as contact details, application text, profile metadata, and email address. A local Git repository does not publish anything. Inspect every diff and decide separately whether to publish it; if you use a remote, make it private and control who can access it. - - - - JSON exports store image URLs, not offline copies of image files. An imported backup can show an image only while the referenced storage remains available and accessible. - - -### Recover an earlier resume without replacing the current one - -Choose the committed revision of `resume.json` that you want to recover. List commits that changed the file, then inspect -the selected revision from the backup folder. Choose a fresh, unused output filename; the example assumes -`recovered-resume.json` does not already exist: - -```bash -git log --oneline -- resume.json -git show :resume.json -git show :resume.json > recovered-resume.json -``` - -On the Reactive Resume dashboard, select **Import an existing resume**, choose `recovered-resume.json`, and complete the import. Import creates a new resume, so the current resume remains available for comparison or further editing. - -Only a single-resume JSON export works for this recovery flow. Import independent cover letters through the cover-letter library. Restoring an account archive wholesale is outside this workflow. - -Git backups complement Reactive Resume's rolling snapshots; they do not replace or synchronize with [undo and version history](/guides/undoing-changes-and-version-history). - -## Printing your resume - -To print, export a **PDF** and print it from your PDF viewer or browser. This gives you the same layout that recruiters see when you send them the file. - -## Public resume downloads - -If your resume is public, visitors can download a PDF from the public resume page. You can enable public access in the builder's **Sharing** section. - -For the full public sharing workflow, see [Sharing your resume publicly](/guides/sharing-your-resume-publicly). +- [Exporting a resume to Markdown](/guides/exporting-resume-to-markdown): what the Markdown file looks like and where it's useful. +- [Writing a cover letter](/guides/writing-a-cover-letter): letters have their own **Download** tab in their Share sheet. +- [Fitting content on a page](/guides/fitting-content-on-a-page): tidy up page breaks before you download. +- [Exporting your data](/guides/exporting-your-data): download everything in your account at once. +- [Importing resumes](/guides/importing-resumes): bring a JSON backup back into Reactive Resume. diff --git a/docs/guides/filling-in-your-details.mdx b/docs/guides/filling-in-your-details.mdx new file mode 100644 index 000000000..785a28fbe --- /dev/null +++ b/docs/guides/filling-in-your-details.mdx @@ -0,0 +1,150 @@ +--- +title: "Filling in your details" +description: "Add your name, headline, contact details, extra links, photo and summary to a Reactive Resume using the Basics card and the Summary section." +--- + +The top of a resume tells a recruiter who you are and how to reach you. In Reactive Resume that information lives in the **Basics** card at the top of the Write panel, with your summary just below it. Everything you type saves automatically and shows on the page as you go. + +## Before you start + +Open a resume in the editor and make sure **Write** is selected in the editor bar. If you're new to the editor, read [Getting to know the resume editor](/guides/editor-overview) first. + +## Enter your name and contact details + + + The Basics card showing the photo row, Full name, Headline, Email, Phone, Location and Website fields, two extra fields with icons, and the Add field button + + + + + The card is at the top of the Write panel and shows your initials, name, headline and location. Select it to open or close it. You can also click your name on the page to jump straight to it. + + + - **Full name**: your name as you want it printed. On a blank resume, this field is ready for typing as soon as the editor opens. + - **Headline**: a short line under your name, usually your job title or the role you're aiming for, for example "Game Developer". + - **Email** and **Phone**: how employers contact you. If an email address looks incomplete, the field tells you what's missing, for example "Add the domain after the @". + - **Location**: a city and region is enough, for example "Seattle, WA". + - **Website**: your personal site or portfolio. You can leave out `https://`; it's added for you. + + + Your details appear in the page header as you type. Leave a field empty to keep it off the page. + + + + + To show friendly text instead of the full web address, select the tag icon inside the **Website** field (**Add a label to the URL**) and type a **Label**, such as "Portfolio". + + +## Add more links and contact details + +Use custom fields for anything the standard fields don't cover, such as a GitHub profile, a portfolio on another site, or a second phone number. + + + + **Add field** is under the **Website** field. A new row appears with an icon, a text box and a few buttons. + + + Enter what should appear on the page, for example `github.com/dkowalski-dev`. + + + Select the icon button at the start of the row (**Pick an icon**), then search for and pick an icon. + + + Select the link button (**Add a link**) and enter the web address. The button is highlighted once the field has a link (its name changes to **Edit link**), and the text on the page becomes clickable. + + + +To reorder custom fields, select the up arrow (**Move field up**) on a row. To delete one, select **×** (**Remove field**). + +For social profiles you want listed in their own section, such as LinkedIn with a username, use the **Profiles** section instead. See [Managing sections](/guides/managing-sections). + +## Add a photo + +Whether to include a photo depends on where you're applying. Many applicant tracking systems ignore photos, while some countries expect one. The photo row at the top of the Basics card reminds you of this. + + + + In the photo row, select **Add** (or **Edit** if the resume already has a photo). The photo settings open. + + + Select the empty square (**Upload picture**) and choose an image from your device. JPEG, PNG, WebP and GIF files up to 10 MB work. + + You can also paste the address of an image that's already online into the **URL** field. + + + The **Crop picture** dialog opens. Drag the image to position it and use the **Zoom** slider to frame it, then select **Crop and Upload**. Select **Skip and Upload** to use the whole image, or **Cancel** to stop. + + The crop frame follows the photo's **Aspect Ratio** setting, so set that first if you want a non-square photo. When **Fit** is set to **Contain**, the image uploads without the crop step. + + + Change the settings below and watch the page update. Your changes save as you make them. + + + + + The photo settings popover with the photo preview, URL field, visibility toggle, Fit, Size, Rotation, Aspect Ratio, Border Radius, Border Width and Shadow Width controls + + +| Setting | What it does | +| --- | --- | +| Eye icon next to **URL** | Hides the photo from the page without deleting it (**Hide picture** / **Show picture**). The photo row then says "Photo hidden from the page." | +| **Fit** | **Cover** fills the frame and trims the edges. **Contain** shows the whole image inside the frame. | +| **Size** | The photo's width on the page, from 32 to 512 pt. | +| **Rotation** | Turns the photo, from 0 to 360°. | +| **Aspect Ratio** | Width divided by height, from 0.5 to 2.5. The three buttons set square (1), landscape (1.5) or portrait (0.5). | +| **Border Radius** | Rounds the corners, from 0 to 100 pt. The buttons set square corners, slightly rounded, or a circle. | +| **Border Width** and color | Draws a border around the photo. Select the color circle to pick its color. | +| **Shadow Width** and color | Adds a shadow behind the photo. Select the color circle to pick its color. | + + + The Crop picture dialog with the photo inside a square crop frame, a Zoom slider, and Cancel, Skip and Upload, and Crop and Upload buttons + + +To remove the photo, open the photo settings and select the photo preview (**Delete picture**). + + + The photo is deleted as soon as you select the preview, without asking first, and every photo setting goes back to its default. To take the photo off the page but keep it, use the eye icon instead. + + + + Uploaded photos are resized to at most 800 × 800 pixels and saved as JPEG, so transparent backgrounds become solid. Self-hosted installations can turn this processing off. + + +## Write your summary + +The summary is a short statement under your header that says who you are and what you offer. + + + + Look for **Summary** in the section list under the Basics card, and select it to open it. + + If there's no Summary row, select **Add section** at the bottom of the list, then **Summary**. + + + Click into the text box and type. While you're typing, the box shows the formatting toolbar, the hint "2–3 sentences reads best" and a character count. + + + + + The Summary section open, with the summary text, the formatting toolbar with an Improve button, and a footer reading 2–3 sentences reads best and 424 characters + + +To keep the summary but leave it off the page, select the eye icon on its row (**Hide Summary from the page**). For bold text, lists and links, see [Formatting text](/guides/formatting-text). + +## Next steps + +- [Managing sections](/guides/managing-sections): add experience, education, skills and more. +- [Editing entries](/guides/editing-entries): fill in jobs, degrees and other entries. +- [Formatting text](/guides/formatting-text): make text bold, add lists and links, and improve a line with AI. diff --git a/docs/guides/fitting-content-on-a-page.mdx b/docs/guides/fitting-content-on-a-page.mdx index b37dd43c1..0935c69b5 100644 --- a/docs/guides/fitting-content-on-a-page.mdx +++ b/docs/guides/fitting-content-on-a-page.mdx @@ -1,166 +1,74 @@ --- title: "Fitting content on a page" -description: "Reorganize sections, tune typography, adjust margins, and trim content to fit your Reactive Resume on a single A4 or Letter page without overflow." +description: "See when your resume runs onto an extra page, fit it back with one click, or tighten text size, density, margins and content yourself." --- -When your resume content overflows the page, Reactive Resume shows a warning just below the page. This guide covers how to reorganize and trim your content so everything fits your chosen page format. +Many employers expect a one-page resume, or two pages at most. Reactive Resume tells you as soon as your content runs past the pages you planned, and can tighten the design for you until it fits. This guide covers the automatic fit and the changes you can make by hand. - - Screenshot of the overflow warning message in the resume builder +## Know when content runs over + +Your layout has a set number of pages (one, unless you add more; see [Arranging the layout](/guides/arranging-the-layout)). When the content needs more than that, three things show it: + +- A warning above the first page says how far it runs over, for example "Runs onto page 2 by about 6 lines", next to a **Fit to one page** button. +- A dashed amber line with the page number marks where each extra page begins. +- The zoom bar at the bottom of the page shows the total number of pages. + + + Above the first page, the label Page 1 next to an amber warning reading Runs onto page 4 by about 35 lines and a button labelled Fit to 3 pages - - While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or - Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed - or exported. - - -## Quick fixes - -The fixes below are ordered from simplest to most involved. - -### 1. Switch to Free-Form format - -If you don't plan on printing your resume, switch to **Free-Form** format. Free-Form creates a single continuous page with no height limit, so nothing can overflow. - -With Free-Form: - -- Your resume renders as one continuous document -- There are no page breaks to manage -- ATS parsers and AI scanners can still read your content -- You can focus on the content instead of page limits - -To switch formats, go to the **Page** section in the right sidebar and change the **Format** to Free-Form. For more details, see [Selecting the right page format](/guides/selecting-page-format). - - - Most resumes are read on a screen, so Free-Form is often the best choice unless you need printed copies. - - -### 2. Shorten text blocks - -Long paragraphs take up space without adding proportional value. Review each section and cut ruthlessly: - -- **Use bullet points** instead of paragraphs. Bullets are easier to scan and take less vertical space. -- **Remove filler words.** "Was responsible for managing" becomes "Managed." -- **Focus on impact.** Keep measurable achievements; cut generic descriptions. -- **Limit bullets per entry.** Three to five bullets per job is usually enough. - -Read each bullet point and ask: "Does this help me get an interview?" If not, cut it. - -### 3. Use multi-column layouts - -Some sections work better in multiple columns, especially lists of short items. - -In the left sidebar, find the section you want to adjust, click on the section heading (not an item), and change the **Columns** setting. - - - Screenshot of the columns setting for a section + + The bottom of page 3 and the top of an extra page 4, separated by a dashed amber line labelled Page 4, with the Projects section at the top of page 4 -Good candidates for multi-column layouts: + + The zoom bar with a minus button, a Fit button, a plus button and the text 4 pages + -| Section | Recommended Columns | -| ------------------------- | ------------------- | -| Skills | > 2 columns | -| Languages | > 3 columns | -| Interests | > 2 columns | -| Profiles | > 3 columns | -| Certifications (if brief) | > 2 columns | +The **Fit** button in the zoom bar (it shows a percentage after you zoom in or out) only zooms the page to fit your screen. It doesn't change your resume. - - Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like - Experience or Projects) usually work better in a single column. - +## Fit it automatically -### 4. Move items to another page +Select **Fit to one page** in the warning (it reads **Fit to 2 pages**, **Fit to 3 pages** and so on when your layout has more pages). Reactive Resume tightens the design one step at a time and checks the result after each step: -If you have more content than fits on one page, move less important items to page two. This keeps your first page focused on your most relevant experience. +1. **Density**: Roomy, then Normal, then Compact. +2. **Margins**: Wide, then Normal, then Narrow. +3. **Text size**: half a point smaller each time, never below 9 pt. -Use the **Move to** feature to relocate items: +If your density or margins are exact values from Advanced rather than one of the presets, Fit goes straight to Compact or Narrow for that step. It stops as soon as everything fits and tells you what it changed, for example "Fits on one page: Compact, narrow margins, 10 pt". Select **Undo** in that message, or press ⌘ Z (Ctrl Z on Windows and Linux), to reverse the whole fit in one step. -1. Open the item's dropdown menu (three-dot icon) -2. Hover over **Move to** -3. Select the destination page +If the content still doesn't fit at 9 pt, the message says so ("Still 2 pages at 9 pt. Hide a section or shorten entries."). At that point, trim the content itself. -For detailed instructions, see [Moving items between sections](/guides/moving-items-between-sections). +## Tighten the design yourself + +You can make the same changes by hand in **Design**, and go further in **Advanced**: + +| Change | Where | Notes | +| --- | --- | --- | +| Smaller text | **Type → Text size** | 10–11 pt reads best. Below 9 pt, Check mode flags the text as too small. | +| Tighter lines and spacing | **Type → Density** | **Compact** saves the most space. | +| Smaller margins | **Page → Margins** | **Narrow** saves the most. Check mode flags margins below 8 pt. | +| Exact values | **Advanced → Typography** and **Advanced → Page** | Line height below 1.15 is flagged by Check mode. | +| Narrower sidebar | **Template → Sidebar → Width** | Gives the main column more room on two-column templates. | +| A different template | **Template** | Some templates fit more on a page. The thumbnails show your content, so compare how full each one looks. | + +See [Customizing typography](/guides/customizing-typography) and [Choosing paper size, margins and language](/guides/selecting-page-format) for details. + +## Trim or rearrange the content + +Design changes only go so far. A resume that fits because its content is focused reads better than one squeezed into small type. + +- **Shorten entries.** Cut filler words, keep measurable results, and aim for three to five bullet points per job. +- **Hide what doesn't help.** Hide older or less relevant entries and sections from the page without deleting them; see [Editing entries](/guides/editing-entries) and [Managing sections](/guides/managing-sections). +- **Put short sections in columns.** Skills, languages and interests take less height in two or three columns; see [Arranging the layout](/guides/arranging-the-layout#show-a-sections-entries-in-columns). +- **Plan a second page.** If two pages are acceptable, add a page and move less important sections onto it, so page one holds your strongest material. The warning then counts two planned pages. - Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older - projects or volunteer work) to subsequent pages. + If your resume will only be read on screen, the **Free-form** paper format makes each page as tall as its content, so nothing ever runs over. See [Choosing paper size, margins and language](/guides/selecting-page-format#free-form-one-long-page). -### 5. Adjust layout and design settings +## Related guides -The right sidebar contains settings that control how much space your content uses. Small adjustments here can make a big difference. - -Open the right sidebar and explore these options: - -| Setting | Where to find it | Effect | -| ----------------- | ---------------------- | ------------------------------------------------------ | -| **Font size** | Typography | Smaller fonts fit more text per line and per page | -| **Line height** | Typography | Tighter line spacing reduces vertical space | -| **Margins** | Page | Smaller margins give you more usable area | -| **Section gaps** | Page | Reducing gaps between sections saves space | -| **Sidebar width** | Layout | Adjusting the sidebar ratio can balance content better | -| **Picture size** | Picture (left sidebar) | A smaller photo leaves more room for text | - - - **Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format. - Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The - editor supports 0.1pt increments, so you can fine-tune precisely. - - -### 6. Hide less important sections - -If you're still short on space, consider hiding sections that aren't essential for your target role: - -- **Interests**: nice to have, but rarely a deciding factor -- **References**: "Available upon request" is assumed, so you don't need to list them -- **Older certifications**: keep only the ones relevant to the job -- **Volunteer work**: include only if it strengthens your application - -To hide a section, click on the section heading in the left sidebar and toggle the **Hidden** switch. - -## Finding the right balance - -If you need to stick with A4 or Letter format, start with content changes (steps 2-4) before adjusting design settings (steps 5-6). A resume that fits its content reads better than one crammed into the space. - -Try this order: - -1. Consider switching to Free-Form if printing isn't required -2. Cut unnecessary text first -3. Reorganize with columns where appropriate -4. Move secondary content to page two if needed -5. Fine-tune font size and spacing last - - - Use the live preview to see changes as you make them. Small adjustments add up. Reducing font size by 0.5pt along - with slightly smaller margins can recover enough space for several lines of content. - - -## Troubleshooting - -### Content still overflows after trying everything - -If you've tried all the above and content still overflows: - -- **Re-evaluate what's essential.** Every item should earn its place. Cut aggressively. -- **Try a different template.** Some templates are more space-efficient than others. - -### The preview looks different from the PDF - -The PDF export matches the preview exactly. If they appear different, try: - -- Refreshing the page -- Checking that all fonts have loaded -- Ensuring your browser zoom is at 100% - -### I made the font too small and now it's hard to read - -Resume fonts should stay between 9pt and 12pt for body text. If you've gone below 9pt to fit content, you're trying to include too much. Go back to step 1 and cut more content instead. +- [Checking your resume](/guides/checking-your-resume): Check mode flags small text, tight margins and other issues. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): go back to an earlier design. +- [Exporting your resume](/guides/exporting-your-resume): download the result as a PDF. diff --git a/docs/guides/formatting-text.mdx b/docs/guides/formatting-text.mdx new file mode 100644 index 000000000..b934a8de4 --- /dev/null +++ b/docs/guides/formatting-text.mdx @@ -0,0 +1,118 @@ +--- +title: "Formatting text" +description: "Use bold, italics, bulleted and numbered lists, and links in your summary and descriptions, clear old formatting, and improve a line with AI." +--- + +Your summary and every description field accept formatted text. Use it to make achievements scannable: a bulleted list under each job, a bold phrase where it matters, a link to a project. This page covers the formatting toolbar, Markdown shortcuts, links, and the **Improve** button. + +## The formatting toolbar + +Click into a summary or description and a toolbar appears under the text. It stays out of the way until the text has focus, and clicking its buttons doesn't move your cursor. + + + The formatting toolbar with Bold, Italic, Link, Bulleted list, Numbered list and Clear formatting buttons, an Improve button, and a character count underneath + + +| Button | What it does | +| --- | --- | +| **Bold** | Makes the selected text bold. | +| **Italic** | Makes the selected text italic. | +| **Link** | Adds, changes or removes a link on the selected text. | +| **Bulleted list** | Turns the current paragraph into a bulleted list, or back into a paragraph. | +| **Numbered list** | Turns the current paragraph into a numbered list, or back into a paragraph. | +| **Clear formatting** | Removes all formatting from the selected text, including lists and alignment. | +| **Improve** | Suggests a better version of the line your cursor is in. Needs an AI provider. See [Improving a line with AI](#improving-a-line-with-ai). | + +A button is highlighted when its formatting applies where your cursor is. Under the toolbar, a footer shows a hint for the field and how many characters it holds. + +### Keyboard and Markdown shortcuts + +You can format without the toolbar: + +| Type or press | Result | +| --- | --- | +| ⌘ B (Ctrl B) | Bold | +| ⌘ I (Ctrl I) | Italic | +| `- ` or `* ` at the start of a line | Starts a bulleted list | +| `1. ` at the start of a line | Starts a numbered list | +| `**text**` | Makes "text" bold | +| `*text*` | Makes "text" italic | + +Press Enter for a new bullet, and Enter twice to end the list. + +Inside a text field, ⌘ Z (Ctrl Z) undoes your typing in that field. Click outside the field to undo changes across the whole resume. + +## Add a link + + + + Highlight the words you want to link, for example a project name. + + + The **Link address** dialog opens with `https://` filled in. + + + Type or paste the full web address, then select **Confirm**. The text becomes a link on the page and in the PDF. + + + + + The Link address dialog with an https:// field, the hint Leave it empty to remove the link, and Cancel and Confirm buttons + + +To change a link, put your cursor in it and select **Link** again. To remove it, clear the address (or leave only `https://`) and select **Confirm**. + +Links don't open when you click them in the editor, so you can click into linked text to edit it. + +## Formatting the toolbar doesn't offer + +The toolbar keeps to formatting that reads well on a resume and survives applicant tracking systems. Text you import, or wrote in an older version of Reactive Resume, may carry other formatting, such as headings, colors, highlights, underlines or centered text. That formatting is kept and still prints. + +To remove it, select the text and choose **Clear formatting**. + +If a description contains a table, the editor keeps the table as it is and shows "Original table formatting is preserved. This content is read-only because it cannot be edited safely." You can still see it on the page, but you can't edit it in the text box. + +## Improving a line with AI + +**Improve** rewrites one line at a time: a single paragraph of your summary or a single bullet. + + + + Click anywhere in the line you want to improve. **Improve** is unavailable until the cursor is in a line with text. + + + Choose **Stronger verb**, **Add a result** or **Make it shorter**, or select **Ask for something else…**, describe what should change, and select **Ask**. + + + Select **Replace** to use it, or **Keep mine** to leave your line as it was. If the suggestion adds facts, it asks you to check they're accurate. + + + +Only that one line changes, and only when you select **Replace**. If you edit the line while the suggestion is loading, it isn't applied. + +Improve needs an AI provider. If you haven't connected one, selecting **Improve** opens the assistant so you can set one up. See [Connecting an AI provider](/guides/using-ai). + +## On a phone + +On screens narrower than 768 pixels, such as phones and small tablets held upright, the toolbar docks to the bottom of the screen, just above the keyboard, with larger buttons. To close the keyboard, select **Done** at the right end of the toolbar, or tap outside the text box. + + + A description being edited on a phone, with the formatting toolbar pinned to the bottom of the screen + + +See [Editing on a phone or tablet](/guides/editing-on-mobile) for the rest of the mobile editor. + +## Related guides + +- [Filling in your details](/guides/filling-in-your-details): the summary and your contact details. +- [Editing entries](/guides/editing-entries): descriptions for jobs, projects and other entries. +- [Using the assistant](/guides/using-the-assistant): larger rewrites and questions about your whole resume. diff --git a/docs/guides/importing-applications-from-csv.mdx b/docs/guides/importing-applications-from-csv.mdx index b2ee94a5f..0046df565 100644 --- a/docs/guides/importing-applications-from-csv.mdx +++ b/docs/guides/importing-applications-from-csv.mdx @@ -1,128 +1,117 @@ --- title: "Importing applications from CSV" -description: "Move existing job applications into the Reactive Resume Application Tracker by uploading a CSV file or pasting spreadsheet rows with headers." +description: "Move job applications from a spreadsheet into Reactive Resume with CSV import, check how columns are matched, and export your applications back to CSV." --- -Use CSV import when you already track applications in a spreadsheet and want to move them into Reactive Resume. - -## Open CSV import - - - - In the dashboard sidebar, click **Applications**. - - - - If you have no applications yet, click **Import from CSV** in the empty state. Otherwise, click **Import CSV** in the - page header. - - - - - CSV import sheet showing upload, pasted CSV rows, recognized fields, and one application ready to import - - -## Prepare your CSV - -The importer uses the first row as headers. Each imported row must include a company and role. - -Supported headers include: - -| Field | Recognized headers | -| --- | --- | -| **Company** | `Company`, `Employer`, `Organization` | -| **Role** | `Role`, `Title`, `Position`, `Job Title` | -| **Stage** | `Stage`, `Status` | -| **Location** | `Location` | -| **Salary** | `Salary`, `Salary Range`, `Compensation` | -| **Source** | `Source` | -| **Notes** | `Notes`, `Note` | -| **Job posting URL** | `URL`, `Link`, `Job URL`, `Job Posting` | -| **Tags** | `Tags` | -| **Contact name** | `Contact Name` | -| **Contact role** | `Contact Role` | -| **Contact label** | `Contact Type` | -| **Contact email** | `Contact Email` | -| **Contact phone** | `Contact Phone` | - -Tags can be separated with commas, semicolons, or vertical bars. - -Each row can carry one contact. A contact needs a `Contact Name`, and `Contact Email` must be a valid email address — if -either is wrong, the contact is dropped and the application still imports. - -```csv -Company,Role,Stage,Location,Salary,Source,Tags,Contact Name,Contact Email,Contact Phone -Stripe,Frontend Engineer,applied,Remote,$180k,LinkedIn,remote;react,Jane Doe,jane@example.com,+1 555 0100 -``` +If you've been tracking your job search in a spreadsheet, you can bring it into **Applications** in one go. Save the sheet as CSV, paste or upload it, check how the columns were matched, and import. You can also export your applications to CSV at any time. ## Import applications - - Upload a `.csv` file, or paste CSV rows directly into the **CSV data** field. + + In **Applications**, select the import/export button (the two arrows next to **Add application**), then **Import from CSV…**. If you have no applications yet, select **Import from CSV** on the empty page. - - - Reactive Resume shows how many rows are ready to import, which columns it recognized, and how many rows were skipped. + + Select **Upload .csv** to choose a file, or paste rows into **CSV data**. The first row must be the column headings. Select **Use sample** to see an example. - - - Rows without a company or role are skipped. Add the missing values before importing if you want those rows included. - The preview also counts skipped contacts — those rows still import, just without the contact. + + Under **Columns**, each heading from your file points to the field it goes into. Reactive Resume matches common headings automatically. Change any match with its menu, or choose **Leave out** to skip a column. + + + The summary says how many applications are ready to import, and how many rows or contacts will be skipped. - - Click **Import**. Imported applications are added to your Application Tracker. + Select **Import *n* applications**. They're added to your Applications right away. -## Use valid stages + + Import from CSV sheet with pasted rows, and a Columns list matching Company to Company, Position to Role, Status to Stage, Applied Date to Stage date, Location, Source, Tags, Contact Name and Contact Email to their fields, and Referrer to Leave out + -The **Stage** column is optional. If you include it, use one of these values: + + Summary reading 2 applications ready to import and 1 row has no company or role and will be skipped, with a Download them link and an Import 2 applications button + -- `saved` -- `applied` -- `screening` -- `interview` -- `offer` -- `rejected` +## Prepare your spreadsheet -If a row has an unrecognized stage, the importer ignores that stage value and uses the default application stage. +Each row becomes one application. Only **Company** and **Role** are required; rows missing either are skipped. Select **Download them** in the summary to get the skipped rows as a CSV file, fix them, and import them again. + +These are the fields you can match columns to, with the headings that match automatically (capitals and extra spaces don't matter): + +| Field | Headings matched automatically | Notes | +| --- | --- | --- | +| **Company** | Company, Employer, Organization | Required. | +| **Role** | Role, Title, Position, Job Title | Required. | +| **Stage** | Stage, Status | One of `saved`, `applied`, `screening`, `interview`, `offer` or `closed`. `rejected` is read as `closed`. Other values are ignored, and rows without a stage import as **Saved**. | +| **Stage date** | Applied Date, Stage Date, Stage Entered At | The date the application entered its stage, as `YYYY-MM-DD`. Other formats are ignored and today's date is used. | +| **Location** | Location | | +| **Salary** | Salary, Salary Range, Compensation | | +| **Source** | Source | Shows in the Insights sources chart. | +| **Link** | URL, Link, Job URL, Job Posting | The job posting's address. | +| **Notes** | Notes, Note | | +| **Tags** | Tags | Separate tags with commas, semicolons or vertical bars, such as `remote;unity`. | +| **Contact name**, **Contact role**, **Contact label**, **Contact email**, **Contact phone** | Contact Name, Contact Role, Contact Type, Contact Email, Contact Phone | One contact per row. | +| **Archived (closes it)** | Archived | For files exported from older versions: `true` imports the application as closed. | + +A contact needs a name, and its email must be a valid address. If not, the summary counts it as skipped and the application imports without it. + +Here's a small example: + +```csv +Company,Role,Stage,Stage Date,Location,Source,Tags,Contact Name,Contact Email +Orchard Lane Games,Gameplay Programmer,applied,2026-09-02,Remote,LinkedIn,remote;unity,Nadia Brooks,nadia@orchardlane.example +Silverpine Interactive,AI Programmer,screening,2026-08-28,Stockholm,Referral,ai,, +``` ## Import large files -Reactive Resume imports up to 500 applications at a time. If your CSV has more than 500 valid rows, split it into smaller files and import each file separately. +Up to 500 applications import at a time. If your file has more, the summary says how many were left out. Split the file and import the rest separately. - Import first, then use the table view to select multiple applications and apply tags, move stages, archive rows, or delete rows in bulk. + After importing, use the List view's checkboxes to tag, move or close many applications at once. See [Tracking job applications](/guides/tracking-job-applications#acting-on-several-applications-at-once). - - MCP clients can also import application rows directly with `import_applications`. See [Managing applications with MCP](/guides/managing-applications-with-mcp) for agent prompt examples. - +## Export applications to CSV + + + + Select the import/export button, then **Export to CSV…**. + + + Under **Applications to export**, choose **Current filters** (the applications matching your current search, closed ones included) or **All applications (including closed)**. + + + Set **Application date from** and **Application date to** to export only applications from that period. Leave them empty to include every date. + + + Check the count of applications to export, then select **Download CSV**. + + + + + Export applications sheet with Applications to export set to Current filters, empty Application date from and Application date to fields, and 11 applications to export + + +The file is named `applications-YYYY-MM-DD.csv` and opens in any spreadsheet app. It has these columns: Company, Role, Stage, Stage Date, Application Date, Location, Salary, Source, URL, Tags, Contacts, Notes, Closed Reason, Stage History, Timeline, Created At and Updated At. + +You can import an exported file again; most columns match automatically. To download everything in your account, including documents, see [Exporting your data](/guides/exporting-your-data). ## Troubleshooting -### Some rows were skipped + + + Every row needs a company and a role. Check that those columns are matched under **Columns**, then fill in the missing values. **Download them** gives you the skipped rows. + + + Pick the right field from the column's menu under **Columns**, or rename the heading in your file to one from the table above. + + + Use one of the stage values listed above, and write dates as `YYYY-MM-DD`, for example `2026-09-02`. + + + Separate tags with commas, semicolons or vertical bars. If you use commas, put the whole cell in double quotes, such as `"remote,unity"`, so the commas aren't read as new columns. + + -Make sure every row has both a company and a role. These fields are required. - -### A contact did not import - -The contact needs a `Contact Name`, and `Contact Email` must be a valid address. The application imports either way — fix -the contact columns and import that row again if you want the contact. - -### A column was not recognized - -Rename the header to one of the recognized names in the table above, then import again. - -### Tags did not split correctly - -Separate tags with commas, semicolons, or vertical bars, such as `remote;react` or `frontend|senior`. - -### A stage did not import - -Use lowercase stage values such as `applied` or `interview`. Custom stages are not supported. +MCP clients can import applications too. See [Managing applications with MCP](/guides/managing-applications-with-mcp). diff --git a/docs/guides/importing-resumes.mdx b/docs/guides/importing-resumes.mdx index 4f4e646a9..c60ec0db1 100644 --- a/docs/guides/importing-resumes.mdx +++ b/docs/guides/importing-resumes.mdx @@ -1,82 +1,106 @@ --- -title: "Importing resumes" -description: "Import a resume from JSON, JSON Resume, PDF, or Microsoft Word files with automatic format detection from the Reactive Resume dashboard." +title: "Importing a resume" +description: "Turn an existing resume into an editable Reactive Resume document from a PDF, Word file, LinkedIn export, JSON Resume or Reactive Resume JSON file." --- -Reactive Resume can create a new resume from several existing file formats. Use import when you are moving from another tool, restoring a backup, or converting an older Reactive Resume file. +If you already have a resume, you don't need to retype it. Import the file and Reactive Resume builds a new, editable resume from it, section by section. Your original file is never changed. -## Supported import formats +## Supported files -| Format | Requires AI integration? | Notes | +Reactive Resume works out the format from the file's contents, so you don't pick a type. + +| File | Needs an AI provider? | Notes | | --- | --- | --- | -| **Reactive Resume (JSON)** | No | Best option for backups exported from the current version of Reactive Resume. | -| **Reactive Resume v4 (JSON)** | No | Use this for files exported from Reactive Resume v4. | -| **JSON Resume** | No | Use this for files that follow the JSON Resume schema. | -| **PDF** | Yes | Reactive Resume asks your configured AI provider to parse the file into structured resume data. | -| **Microsoft Word** | Yes | Supports Word documents. Reactive Resume asks your configured AI provider to parse the document. | +| **Reactive Resume JSON** (versions 5 and 6) | No | Best for backups and moving between accounts or servers. Keeps everything, including design. | +| **Reactive Resume v4 JSON** | No | Exports from the old version 4 app. | +| **JSON Resume** (`.json`) | No | Files that follow the open [JSON Resume](https://jsonresume.org) standard. | +| **LinkedIn data export** (`.zip`) | No | Reads your profile, positions, education, skills, languages and certifications. | +| **PDF** | Optional | Read by your AI provider if you've set one up; otherwise read in your browser. | +| **Word** (`.docx`) | Yes | Read by your AI provider. | + +A cover letter exported from Reactive Resume as JSON imports as a letter, not a resume. - PDF and Word imports depend on your AI settings because those formats are not structured resume data. Configure AI in - **Settings → AI & developer** before using those import types. + An AI provider is an AI service you connect in **Settings → AI & developer**. Only a provider that is turned on and has passed its connection test counts. See [Connecting an AI provider](/guides/using-ai). -## Import a resume +## Import a file - - Sign in and go to **Dashboard → Resumes**. + + On Documents, select **New** in the sidebar (or press N), then select **Import a resume** and pick your file. On your first visit, the empty Documents page has a **Choose a file** button that opens the same dialog. + + Drag the file from your computer anywhere onto the Documents page. The page shows **Drop to import**; let go and the import starts. - - Click the **Import** card (or the header button if you already have resumes). - - - Import dialog showing the file picker and detected import format + + The Documents page covered by a green drop area reading Drop to import, PDF, Word or JSON. We'll build a resume from it - - - Select the resume file from your computer. Reactive Resume detects the format automatically from the file's contents - and shows the matching import type. - - If auto-detection is uncertain, or you want to force a specific format, adjust the import type manually before continuing. + + The dialog shows **Reading the file**, **Finding sections** and **Filling in entries**, with a note beside each as it finishes. JSON and LinkedIn files take a moment; PDF and Word files read by an AI provider can take longer. Select **Cancel** to stop. + + When the import finishes, the dialog tells you how many sections and entries it found. Select **Open in editor** to start reviewing, or **Stay here** to keep working on Documents. The new resume has a **New** badge on its card. - - Click **Import**. Reactive Resume creates a new resume and opens it in the builder when the import succeeds. - - For PDF and Microsoft Word files, the dialog checks that you have a working AI provider configured before it starts. - If none is available, you'll get a link to **AI & developer** settings to set one up. + + The Importing dialog for David-Kowalski-Resume.pdf with all three steps checked, 9 sections and 58 entries found, and Stay here and Open in editor buttons + -## Choose the right import type +## Review what came in -If you have a file exported from Reactive Resume, choose **Reactive Resume (JSON)**. This preserves the most information because the file already matches Reactive Resume's data model. +When you open an imported resume from the dialog, the **Write** panel starts with a note naming the file you imported and counting its sections and entries. -If you are coming from Reactive Resume v4, choose **Reactive Resume v4 (JSON)**. + + A green note reading Imported from David-Kowalski-Resume.pdf. 9 sections, 58 entries. Everything was read clearly + -If your file is a standard JSON Resume document, choose **JSON Resume**. +If some dates couldn't be read exactly, the note says how many fields need a look, and each of those date fields shows the text it found, for example *We couldn't read "Summer 2019"*. Pick the right month and year so the entry sorts and prints consistently; the count goes down as you fix them. Close the note with **×** when you're done. -If you only have a PDF or Word document, choose **PDF** or **Microsoft Word**. After import, review every section carefully. AI parsing can save time, but it can also miss details, change wording, or place content in the wrong section. +Go through every section before you send the resume. PDF and Word files don't store which text belongs where, so parts can land in the wrong section, lose formatting or come out slightly reworded. Structured files (Reactive Resume JSON, JSON Resume, LinkedIn) come in far more reliably. - - Always review an imported resume before sharing or exporting it. This is especially important for PDF and Word imports. - +## Notes on each format + + + + Download it from another resume with **Download** and the JSON format (see [Exporting your resume](/guides/exporting-your-resume)). Custom styles written for the older styling system come in converted to the current one. If a version 5 resume contained cover letters, each one becomes its own letter in Documents, linked to the imported resume. + + + In LinkedIn's settings, request a copy of your data and download the archive when LinkedIn emails you. Import the `.zip` file as it is; don't unzip it. Reactive Resume reads `Profile.csv`, `Positions.csv`, `Education.csv`, `Skills.csv`, `Languages.csv` and `Certifications.csv` and ignores everything else, such as messages. The file never leaves your browser. + + + With an AI provider set up, the PDF (up to 10 MB) is sent to that provider, which returns the resume's sections. Without one, Reactive Resume reads the PDF's text in your browser and sorts it into sections itself. This works best for simple, one-column resumes. A scanned PDF (a picture of a page) has no text to read and can't be imported this way. + + + Word files are always read by your AI provider, so set one up first. Use the modern `.docx` format; if you have an older `.doc` file, open it in Word and save it as `.docx`. Files can be up to 10 MB. + + ## Troubleshooting -### The PDF or Word import says AI must be enabled +**"This file type can't be imported."** The file isn't one of the supported types. Export your resume as PDF, Word or JSON, or use your LinkedIn data export. -Open **Settings → AI & developer**, fill in your AI provider settings, test the connection, and enable AI features. +**"Reading Word files needs an AI provider."** Connect a provider in **Settings → AI & developer** and test it, or import a PDF or JSON version instead. **Choose another file** lets you pick a different file without closing the dialog, and **Start blank** gives you an empty resume. -### The imported resume is incomplete + + The Couldn't import dialog for Resume.docx saying Reading Word files needs an AI provider, with Start blank and Choose another file buttons + -Try importing a cleaner source file. Simple resumes with selectable text import more reliably than scanned documents, image-heavy PDFs, or files with complex tables. +**"This PDF is a scanned image."** Try the Word version of the resume, set up an AI provider, or start blank and paste your sections in. -### The JSON file is rejected +**"This ZIP doesn't look like a LinkedIn data export."** Make sure you're importing the archive LinkedIn sent you, not a ZIP you made yourself. -Make sure you selected the correct JSON import type. A Reactive Resume JSON export, a Reactive Resume v4 JSON export, and a JSON Resume file are different formats. +**"This PDF is password protected."** Save a copy without a password and import that instead. + +**"The file couldn't be read as a resume"** (or **"The file could not be read as a valid resume"**, followed by the fields it couldn't read). The JSON file is damaged or isn't a resume. Check that it came from Reactive Resume or follows the JSON Resume standard. + +**"Couldn't reach the AI provider."** Your provider didn't answer. Try again in a moment, or test the connection in **Settings → AI & developer**. + +## Related guides + +- [Creating your first resume](/guides/creating-your-first-resume): start from scratch instead. +- [Entering dates](/guides/entering-dates): fix dates that need a look. +- [Managing sections](/guides/managing-sections): rename, hide or reorder imported sections. +- [JSON Resume schema](/guides/json-resume-schema): the structure of Reactive Resume's JSON format. diff --git a/docs/guides/keyboard-shortcuts.mdx b/docs/guides/keyboard-shortcuts.mdx new file mode 100644 index 000000000..5fff98086 --- /dev/null +++ b/docs/guides/keyboard-shortcuts.mdx @@ -0,0 +1,111 @@ +--- +title: "Keyboard shortcuts" +description: "The keyboard shortcuts in Reactive Resume: the command bar, Documents, the resume and letter editors, text formatting, the assistant and proposed edits." +--- + +This page lists the keyboard shortcuts in Reactive Resume, grouped by where they work. + +On a Mac, use ⌘ (Command). On Windows and Linux, use Ctrl wherever this page shows ⌘. +The app's tooltips and buttons show the Mac symbols on every system. + + + Shortcuts made of a single key, such as N or 1, don't work while you're typing in a text + field, so they never get in the way of your writing. Shortcuts with ⌘ work everywhere. + + +## Everywhere + +| Shortcut | What it does | +| --- | --- | +| ⌘ K | Open or close the [command bar](/guides/using-the-command-bar). | +| Esc | Close the command bar. | +| ↑ ↓, Enter | In the command bar, move between rows and run the selected one. | + +## Documents, Applications and Settings + +| Shortcut | What it does | +| --- | --- | +| N | Open the **New document** dialog. | +| / | Jump to the search box on **Documents**. | +| Enter | Save a document name while renaming it on its card. | +| Esc | Cancel renaming a document on its card. | + +## Resume editor + +| Shortcut | What it does | +| --- | --- | +| 1 | Switch to **Write**. | +| 2 | Switch to **Design**. | +| 3 | Switch to **Check**. | +| ⌘ Z | Undo your last change. | +| ⇧ ⌘ Z | Redo. | +| Ctrl Y | Redo (Windows and Linux). | +| ⌘ P | Download the PDF. This replaces the browser's print dialog; to print, use **Print** in the document menu. | +| ⇧ ⌘ S | Open **Share & export** on the **Link** tab. | +| ⇧ ⌘ E | Open **Share & export** on the **Download** tab. | +| ⌘ J | Open or close the assistant. | +| ⌘ 0 | Fit the page to the window. | +| ⌘ S | Nothing to do: a message reminds you that changes are saved automatically. | +| Esc | Clear the block selected on the page. In **Design**, also stop previewing a template. | +| ⌥ ↑ / ⌥ ↓ | With a section or entry title focused in **Write**, move it up or down (use Alt on Windows and Linux). | + +While your cursor is in a text field, ⌘ Z and ⇧ ⌘ Z undo and redo +your typing in that field instead of the last change to the resume. + +The download and share shortcuts do nothing while you're offline, because they need a connection. + +## Letter editor + +| Shortcut | What it does | +| --- | --- | +| 1 | Switch to **Write**. | +| 2 | Switch to **Design**. | +| ⌘ P | Download the PDF. | +| ⇧ ⌘ E | Open **Share & export** on the **Download** tab. | +| ⌘ J | Open or close the assistant. | +| ⌘ 0 | Fit the page to the window. | +| ⌘ S | Send any unsaved edits right away, with a reminder that changes are saved automatically. | + +## Formatting text + +These work in rich text boxes, such as a summary or an entry's description. See +[Formatting text](/guides/formatting-text) for the toolbar. + +| Shortcut | What it does | +| --- | --- | +| ⌘ B | Bold. | +| ⌘ I | Italic. | +| ⇧ ⌘ 8 | Bulleted list. | +| ⇧ ⌘ 7 | Numbered list. | +| `- ` or `* ` at the start of a line | Start a bulleted list. | +| `1. ` at the start of a line | Start a numbered list. | +| `**text**` | Make the text bold. | +| `*text*` | Make the text italic. | +| Esc | Close the **Improve selected line** panel. | + +In fields that collect keywords, press Enter or , to add the keyword you typed. + +## Assistant + +| Shortcut | What it does | +| --- | --- | +| Enter | Send your message. | +| ⇧ Enter | Start a new line in your message. | +| Esc | Stop the assistant while it's replying. | + +## Proposed edits + +When the assistant or Check proposes edits, select an edit in the list to focus it, then: + +| Shortcut | What it does | +| --- | --- | +| ↑ / ↓ | Move to the previous or next edit. | +| A | Accept the focused edit. | +| R | Reject the focused edit. | + +## Related guides + +- [Using the command bar](/guides/using-the-command-bar): search, jump anywhere and ask the assistant from one box. +- [Editor overview](/guides/editor-overview): the editor bar and the three modes these shortcuts switch between. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): undo, redo and restoring + earlier versions. diff --git a/docs/guides/large-rpc-requests.mdx b/docs/guides/large-rpc-requests.mdx index 76de261f1..bcb823d96 100644 --- a/docs/guides/large-rpc-requests.mdx +++ b/docs/guides/large-rpc-requests.mdx @@ -1,27 +1,30 @@ --- title: "Large RPC requests" -description: "Reference for the staged-body protocol that lets RPC requests larger than the hosting request-body limit reach Reactive Resume on Vercel." +description: "Reference for the staged-body protocol that lets RPC requests larger than Vercel's 4.5 MB request limit reach a Reactive Resume installation." --- -Vercel limits Function request bodies to 4.5 MB. On Vercel installations, RPC requests larger than that are uploaded to private Blob staging first. The server then restores the original request and runs it with the normal authorization, validation, and quota checks. +Vercel limits the request body of a Function to 4.5 MB. On a Reactive Resume installation hosted on Vercel, larger RPC requests, such as big file uploads or assistant attachments, are first uploaded to private Blob storage. The server then restores the original request and runs it with the usual authentication, validation and limits. -The web app uses this protocol automatically for request bodies of 3 MiB or more. Docker installations do not need it. +The web app does this for you: it stages any RPC request body of 3 MiB or more, and sends smaller bodies directly. You only need this page if you write your own client for `/api/rpc` against a Vercel installation. ## Scope | Item | Value | | --- | --- | | Applies to | `POST /api/rpc/...` only | -| Does not apply to | REST (`/api/openapi`) and MCP. Their bodies stay subject to the 4.5 MB limit. | +| Does not apply to | The REST API (`/api/openapi`) and MCP (`/mcp`). Their bodies stay subject to the 4.5 MB limit on Vercel. | +| Available on | Installations running on Vercel with Blob storage (`STORAGE_BACKEND=blob`, the default there). Everywhere else the prepare step returns `404` and you send the request directly. Staging also needs Redis; without it the prepare step returns `503`. | | Maximum staged size | 160 MiB of serialized request bytes, including base64 and RPC framing | -| Reference lifetime | Upload URL: 5 minutes. Staging reference: 10 minutes. | -| Use count | One. A reference is consumed when a finalization request passes the user and path checks, whether the RPC call then succeeds or fails. | -| Rate limit | 30 staging requests per user per minute | +| Lifetimes | Upload URL: 5 minutes. Staging reference: 10 minutes. | +| Use count | One. The reference is used up as soon as a finalize request passes the user and path checks, whether the RPC call then succeeds or fails. | +| Rate limit | 30 prepare requests per user per minute | ## Protocol ### 1. Prepare +Tell the server what you are about to send. + ```http POST /api/storage/stage Content-Type: application/json @@ -33,10 +36,10 @@ x-api-key: YOUR_API_KEY | Field | Type | Description | | --- | --- | --- | | `path` | string | Pathname and query string of the original RPC request. Must start with `/api/rpc`. | -| `contentType` | string | `Content-Type` header of the original request, including any multipart boundary. | -| `size` | integer | Exact byte length of the serialized original body. | +| `contentType` | string | The original request's `Content-Type` header, including any multipart boundary. | +| `size` | integer | Exact byte length of the serialized original body. At most 167,772,160 (160 MiB). | -Authenticate with a session cookie, an `x-api-key` header, or an OAuth bearer token. Browsers must send an `Origin` header that matches the application origin. +Authenticate with an `x-api-key` header, an OAuth bearer token or a session cookie. If the request carries an `Origin` header, it must match the installation's own origin. Response `200`: @@ -44,10 +47,12 @@ Response `200`: { "id": "3f2b9c1e-6a0d-4a57-9d0a-3c1f7b8e2d44", "url": "https://..." } ``` -The endpoint returns `404` when the installation does not support staging, for example on Docker. Send the original request unchanged in that case. +If you get `404`, the installation doesn't stage bodies (for example, it runs in Docker). Send the original request unchanged, and skip staging for later requests too. ### 2. Upload +Upload the exact body bytes to the URL from step 1. + ```http PUT Content-Type: application/octet-stream @@ -55,11 +60,11 @@ Content-Type: application/octet-stream ``` -Do not send application credentials to this URL. The body must be exactly `size` bytes. +Don't send your Reactive Resume credentials to this URL. The body must be exactly `size` bytes, sent as `application/octet-stream`. ### 3. Finalize -Send the original request with an empty body and the staging reference header: +Send the original request with an empty body and the staging reference in the `x-resume-staged-body` header: ```http POST /api/rpc/storage/uploadFile @@ -67,21 +72,26 @@ x-api-key: YOUR_API_KEY x-resume-staged-body: 3f2b9c1e-6a0d-4a57-9d0a-3c1f7b8e2d44 ``` -Use the same `path` and the same user as in step 1. The server replaces the body with the staged bytes, sets `Content-Type` to the stored `contentType`, and returns the normal RPC response. +Use the same `path` (including the query string) and the same user as in step 1. The server replaces the body with the staged bytes, sets `Content-Type` to the stored `contentType`, deletes the staged object and returns the normal RPC response. ## Errors | Status | Step | Cause | | --- | --- | --- | -| `400` | Prepare | Invalid JSON, `path` outside `/api/rpc`, or `size` above the maximum. | -| `400` | Finalize | Malformed reference, non-`POST` request, or staged object missing. | -| `401` | Prepare, finalize | Not authenticated, or `Origin` does not match. | -| `403` | Finalize | Reference belongs to another user or another path. | -| `404` | Prepare | Staging not available on this installation. | -| `409` | Finalize | Reference used by a parallel request. | -| `410` | Finalize | Reference expired or already used. | -| `413` | Finalize | Uploaded byte count differs from `size`. | +| `400` | Prepare | Invalid JSON, a `path` outside `/api/rpc` or on another origin, or `size` above the maximum. | +| `400` | Finalize | Malformed reference, a method other than `POST`, staging not available on this installation, or the staged object is missing. | +| `401` | Prepare, finalize | Not authenticated, or the `Origin` header doesn't match. | +| `403` | Finalize | The reference belongs to another user or another path. | +| `404` | Prepare | Staging isn't available on this installation. | +| `409` | Finalize | A parallel request already used the reference. | +| `410` | Finalize | The reference expired or was already used. | +| `413` | Finalize | The uploaded byte count differs from `size`. | | `429` | Prepare | Rate limit exceeded. | -| `503` | Prepare | Redis unavailable. | +| `503` | Prepare | Redis isn't available. | -A reference cannot be retried. If a finalization response is lost, check the result of the mutation before you stage and send it again. +A reference can't be retried. If a finalize response is lost, check whether the change was saved before you stage and send the request again. + +## Related pages + +- [Using the API](/guides/using-the-api): the REST API, which most integrations should use. +- [Deploying to Vercel](/self-hosting/vercel): hosting requirements, including Blob storage and Redis. diff --git a/docs/guides/linking-social-accounts.mdx b/docs/guides/linking-social-accounts.mdx index ad8bbe256..da413b6e7 100644 --- a/docs/guides/linking-social-accounts.mdx +++ b/docs/guides/linking-social-accounts.mdx @@ -1,37 +1,48 @@ --- title: "Linking social accounts" -description: "Connect or disconnect social sign-in providers like Google and GitHub to your Reactive Resume account so you can sign in without a password." +description: "Connect Google, GitHub or LinkedIn to your Reactive Resume account so you can sign in with them, and disconnect a provider you no longer use." --- +Linking a social account lets you sign in with one click instead of typing a password. You can link several providers to the same account and disconnect them at any time. + +## Before you start + +Social sign-in only works when the site has set it up. On [rxresu.me](https://rxresu.me) the available providers appear in **Settings → Account**. On a self-hosted copy you may see none, some, or a provider named after your organization's sign-in service. If a provider isn't listed, it isn't available on that site. + +## Connect a provider + - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. + + Select your name at the bottom of the sidebar, then **Settings**. On the **Account** page, scroll to **Sign-in & security**. - - Open Settings from your avatar and choose Account. Everything about signing in is under **Sign-in & security**. - - - - On the Authentication page, you may see sections for one or more providers (for example, Google or{" "} - GitHub), depending on what is enabled on your instance. - - - - Click the Connect button for the provider you want to link. You'll be redirected to the provider to authorize access, then returned to the Authentication settings page. - - - After a successful link, the button will change to Disconnect. - - + + Each available provider has its own row, such as **Google**, **GitHub** or **LinkedIn**. Select **Connect** next to the one you want. - - To unlink a provider, click Disconnect next to the connected account. - - - Before disconnecting, make sure you still have another way to sign in (for example, a password or another linked provider) so you don't get locked out. - - + + Sign in to the provider if asked, and allow access. You return to **Settings → Account**, and the row now reads **Connected**. + + + The Sign-in and security section with Password, Two-step verification and Passkeys rows, followed by Google marked Not connected with a Connect button, GitHub marked Connected with a Disconnect button, and LinkedIn marked Not connected with a Connect button + + +From now on, select that provider's button on the sign-in page to get in. + + + If you sign in with Google, GitHub or LinkedIn using the same email address as your existing account, Reactive Resume links the provider to that account for you. + + +## Disconnect a provider + +In **Sign-in & security**, select **Disconnect** next to a connected provider. You can no longer sign in with it, but your account and documents stay as they are. + +You can't disconnect your last sign-in method. If you signed up with a social account and have only one provider, [set a password](/guides/updating-your-profile#change-your-password) or connect another provider first. A passkey doesn't count here. + +## Related guides + +- [Signing in](/guides/signing-in): all the ways to get into your account. +- [Setting up passkeys](/guides/setting-up-passkeys): sign in with your fingerprint, face or device PIN. +- [Single sign-on for self-hosters](/self-hosting/sso): how the site owner turns providers on. diff --git a/docs/guides/managing-an-application.mdx b/docs/guides/managing-an-application.mdx new file mode 100644 index 000000000..523978539 --- /dev/null +++ b/docs/guides/managing-an-application.mdx @@ -0,0 +1,141 @@ +--- +title: "Managing an application" +description: "Use an application's details sheet to change its stage, plan the next step, see what you sent, keep notes and contacts, and close or delete it." +--- + +Everything about one application lives in its details sheet. Select an application in the List, Board or Calendar view to open it. The sheet slides in from the right (on a phone, it fills the screen). + + + Details sheet for Senior Gameplay Programmer at Northwind Games, showing the stage stepper, Next step, What you sent, salary, source, applied date, contact, tags, notes, and the Close application… and Prepare for next step buttons + + +At the top you see the role, the company and location, and **View posting** when a posting was saved. **View posting** shows the saved posting text and what the job asks for, or opens the job link if only a link was saved. + +## Moving to another stage + +The row of bars under the title is the stage stepper: **Saved**, **Applied**, **Screening**, **Interview** and **Offer**. Below it you see the current stage and how long the application has been there. + +- To move one stage forward, select **Move to *next stage*** (for example **Move to Offer**). +- To jump to any stage, forwards or back, select its name in the stepper. + + + Header of the details sheet with the stage stepper filled up to Interview, the text Interview for 8 days, and a Move to Offer button + + +Each move is added to the activity history with today's date. You can change that date later (see [Keeping notes and history](#keeping-notes-and-history)). + +## Planning the next step + +**Next step** shows what this application needs now. Reactive Resume works it out in this order: + +1. The next interview that hasn't finished yet. +2. Otherwise, the follow-up you set. +3. Otherwise, for **Applied**, how long ago you applied, turning into "No reply in *n* days" after 10 days; for later stages, how long you've been waiting to hear back. + +A **Saved** application shows "Not applied yet", and a closed one shows its reason. Overdue follow-ups and long waits are highlighted in amber. + + + Next step card showing a Technical interview on Fri, Oct 2 at 4:00 PM with a video link, an Edit button and Add to calendar + + +Select **Edit** to **Schedule an interview…**, **Edit this interview…**, or **Set a follow-up…** (**Change the follow-up…** when one is set). When the next step has a date, **Add to calendar** downloads it as an `.ics` file. See [Scheduling interviews](/guides/scheduling-interviews). + +## Seeing what you sent + +**What you sent** shows the resume and cover letter linked to this application. + +- Before the application reaches **Applied**, a linked document reads "Linked · not sent yet". +- Once it reaches **Applied** or later, Reactive Resume saves the version you sent. The row then reads "Version sent *date*", plus the resume's Check score at that moment. +- **Open** opens that sent version, read-only, in the document's history. Select **Back to now** there to return to the latest version. + + + What you sent section with Game Developer Resume — Northwind Games, Version sent Sep 8 · Check 86, an Open button, a Write a letter button and the Attach a file instead link + + +If nothing is linked yet, **Tailor a resume** and **Write a letter** create one for this job. See [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job). To link a resume you already have, open **⋯** > **Edit details…** and pick it under **Resume**. + +### Attaching a PDF instead + +If you sent a file made elsewhere, select **Attach a file instead**, then **Attach a resume file (PDF)** or **Attach a cover letter file (PDF)**. Only PDF files are accepted. Select the file name to open it, or **×** to remove it. + + + What you sent section with Tailor a resume and Write a letter buttons above two dashed fields: Attach a resume file (PDF) and Attach a cover letter file (PDF) + + +## Editing the key facts + +Below **What you sent** are four facts: + +- **Salary** and **Source**: select the value to edit it in place. Press Enter or select elsewhere to save, or Esc to cancel. +- **Applied**: the date you applied (a dash for **Saved** applications). +- **Contact**: the first contact's name, with "and *n* more" when there are others. + +### Contacts + +Select the **Contact** value to see every contact. Select **Add contact**, then enter a **Name** and, if you like, a role, a label (such as Recruiter, Hiring manager, Referral or Interviewer), an email and a phone number. Emails and phone numbers become links you can select. Select **×** next to a contact to remove it. + + + Contacts popover listing Priya Shah, Recruiter, Talent Partner with an email link, and Marcus Lee, Hiring manager, Lead Gameplay Engineer, with an Add contact button + + +### Tags + +Type in **Add a tag** and press Enter to add a tag. Select **×** on a tag to remove it. Tags are searchable from the Applications page. + +## Keeping notes and history + +**Notes** is a free-form space for anything to remember about the job. It saves automatically a moment after you stop typing, and also when you close the sheet. + +**Activity** is the dated history of the application: stage changes, notes and interviews, newest first. + +- To add a dated note, type in **Add a note…** and select **Add**. +- To change an entry, hover over it, open its **⋯** menu and select **Edit…** (for a note, you can change the text and the date; for a stage change, the date). For an interview, the menu has **Edit interview…** instead, or select the interview itself. +- To remove an entry, select **Delete…** in the same menu and confirm. The entry that marks when the application entered its current stage can't be deleted. + + + Tags, Notes and Activity sections of the details sheet, with an Add a note field and a history of interviews, stage changes and a note, above the Close application… and Prepare for next step buttons + + +Dates matter: Insights uses stage dates to work out how quickly people reply. If you add an application after the fact, adjust the dates so they match what happened. + +## Editing all details + +Open **⋯** at the top of the sheet and select **Edit details…** to edit everything in one form: **Company**, **Role / title**, **Location**, **Salary range**, **Source**, **Stage**, **Job posting link**, the linked **Resume** or an uploaded resume PDF, a **Cover letter** PDF, **Tags**, **Follow-up date**, **Follow-up note** and **Notes**. Select **Save changes** when you're done. + +If you've [connected an AI provider](/guides/using-ai), the form also has a **Job description** section at the top. Select it to expand it, then paste the full posting there and Reactive Resume fills in the company, role, location and salary; select **Fill fields** to run it again. + + + Edit application sheet with a Job description section, Company, Role / title, Location, Salary range, Source, Stage, Job posting link, Resume with Game Developer Resume linked, Cover letter and Tags fields + + +## Closing an application + +When an application ends, close it rather than deleting it, so its history still counts in Insights. + + + + It's at the bottom of the details sheet. + + + Pick **Not selected**, **I withdrew**, **Accepted another offer** or **No response**. + + + Select **Close application**. Linked documents aren't changed. + + + + + Close this application dialog with the reasons Not selected, I withdrew, Accepted another offer and No response, and Cancel and Close application buttons + + +You can also close applications from a board card's **⋯** menu, or several at once from the List view. To bring a closed application back, turn on **Show closed**, open it and select **Reopen**. It returns to **Applied**. + +## Deleting an application + +Open **⋯** at the top of the sheet, select **Delete…** and confirm. The application, its history and any PDF files you attached to it are deleted permanently; there is no Trash for applications. Your resumes and letters are not deleted. + +## Related guides + +- [Scheduling interviews](/guides/scheduling-interviews): add interviews and follow-ups, and put them in your calendar. +- [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job): tailor a resume, write a letter, and prepare for the next step. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): how sent versions appear in a document's history. diff --git a/docs/guides/managing-applications-with-mcp.mdx b/docs/guides/managing-applications-with-mcp.mdx index dff6d069c..5050dddda 100644 --- a/docs/guides/managing-applications-with-mcp.mdx +++ b/docs/guides/managing-applications-with-mcp.mdx @@ -1,298 +1,181 @@ --- title: "Managing applications with MCP" -description: "Use a connected MCP client to track job applications, attach sent PDFs, run Application Copilot, and manage your pipeline with natural-language prompts." +description: "Track job applications from an AI client connected to Reactive Resume: add roles, move stages, schedule interviews, attach sent PDFs and draft letters." --- -Use this guide when you want an AI client to manage the **Application Tracker** for you. After the Reactive Resume MCP server is connected, the agent can use tools for the same application workflows available in the app: listing applications, creating records, importing rows, updating stages, adding notes, managing follow-ups, attaching documents, and running Application Copilot. +Once your AI client is connected to the Reactive Resume MCP server, you can run your job search from a chat: add roles you find, move applications through their stages, log notes and interviews, attach the PDFs you sent, and ask for match scores or tailored resumes. This guide gives you prompts that work well and explains what each one does behind the scenes. -## Prerequisites +## Before you start - - - Follow [Using the MCP server](/guides/using-the-mcp-server) to connect your MCP client with OAuth or an API key. - +- Connect your client to the MCP server with OAuth or an API key. See [Using the MCP server](/guides/using-the-mcp-server). +- Ask the client to list its Reactive Resume tools. You should see `list_applications`, `create_application`, `update_application` and the other application tools. +- For match scores, tailored resumes, drafted letters and job-posting autofill, set up and test a default AI provider in Reactive Resume. See [Connecting an AI provider](/guides/using-ai). These tools send your resume and the job description to that provider. - - Ask your client to list the Reactive Resume tools. You should see tools such as `list_applications`, `create_application`, `update_application`, `attach_application_document`, and `draft_application_message`. - +## How applications are organized - - Tell your agent to ask before deleting applications, bulk-updating many records, or replacing attached documents. - - +Every application sits in one stage: `saved`, `applied`, `screening`, `interview`, `offer` or `closed`. Closing an application takes a reason: `not-selected`, `withdrew`, `accepted-other` or `no-response`. Each application also has a timeline of stage changes, notes and interviews, optional contacts, tags, a follow-up date, a linked resume and letter, and the resume and cover-letter PDFs you sent. - - Application Copilot tools require the same AI provider setup used by the app. Match scoring and resume tailoring work best when the application has a linked Reactive Resume and a job description. - +These are the same applications you see in the app's List, Board, Insights and Calendar views. See [Tracking job applications](/guides/tracking-job-applications). -## Start with a pipeline review +## Review your pipeline first -Have the agent inspect your current pipeline first. That gives it valid application IDs and avoids duplicate records. +Start each session by having the client read what's already there. That gives it valid IDs and stops it from creating duplicates. ```text -List my active applications grouped by stage. Include company, role, tags, follow-up date, linked resume name, and whether a resume or cover-letter PDF is attached. -``` - -Useful review prompts: - -```text -Show me applications that need follow-up this week. +List my applications grouped by stage. For each one show company, role, tags, follow-up date and linked resume. ``` ```text -Find applications tagged remote that are still in saved or applied stage. +Which applications have a follow-up date this week? ``` ```text -Summarize my pipeline stats by stage and source, then point out stale applications. +Show my pipeline counts by stage and by source. +``` + +The client uses `list_applications` (which can filter by stage and tags), `read_application` and `get_application_stats`. + +## Add applications + +When you know the details, ask for the application directly. Only `company` and `role` are required. + +```text +Add an application for Senior Gameplay Engineer at Northwind Games. Stage: saved. Location: Remote. Source: LinkedIn. Tags: unreal, senior. Link my Game Developer Resume. +``` + +If you have a job posting, paste it and ask the client to read it first. The autofill tool works on pasted text; it doesn't open links. + +```text +Read this job posting with the Reactive Resume autofill tool, then save it as an application in the saved stage with the job description included. + +[Paste the job posting here] +``` + +To bring in many rows at once, paste them and ask for an import. `import_applications` takes up to 500 rows per call. + +```text +Import these rows as applications. Map "Rejected" to the closed stage with the reason not-selected. Skip rows without a company or role and tell me which you skipped. + +Company,Role,Stage,Location,Source +Northwind Games,Gameplay Engineer,applied,Remote,LinkedIn +Blue Harbor Studio,Technical Designer,interview,Seattle,Referral +``` + +For spreadsheets you can also import a CSV file in the app. See [Importing applications from CSV](/guides/importing-applications-from-csv). + +## Move stages and add notes + +```text +Move my Northwind Games application to screening and add a note: recruiter call went well, next step is a technical interview. ``` ```text -List archived applications from the last 90 days. -``` - -## Create applications - -Use `create_application` when you already know the role details. - -```text -Create an application for Senior Product Engineer at Acme. Stage: saved. Location: Berlin or remote. Source: LinkedIn. Tags: remote, typescript, senior. Add a note that I want to tailor my platform resume before applying. -``` - -If you have a job posting, ask the agent to extract details first. - -```text -Use the Reactive Resume application auto-fill tool on this job posting URL, then create a saved application from the extracted company, role, location, salary, and job description. Tag it with remote and backend. -``` - -If the posting is private, paste the description into your prompt: - -```text -Create an application from this pasted job description. Use auto-fill if available, keep the stage as saved, and tag it with ai, platform, and high-priority. - -[Paste the job description here] -``` - -## Import applications - -Use `import_applications` when you already have spreadsheet rows. The Application Tracker accepts up to 500 imported rows at a time. - -```text -Import these application rows into Reactive Resume. Normalize the stages to saved, applied, screening, interview, offer, or rejected. Skip rows that do not have both a company and role, and tell me what was skipped. - -Company,Role,Stage,Location,Source,Tags -Acme,Frontend Engineer,applied,Remote,LinkedIn,remote;react -Globex,Staff Engineer,interview,Berlin,Referral,staff;platform +Close the Blue Harbor Studio application with the reason withdrew. ``` ```text -I am pasting rows from my spreadsheet. Import them, tag every imported application with migrated-2026, and leave archived as false. +Add the recruiter Priya Shah (priya@example.com) as a contact on the Northwind Games application. ``` -```text -Import these rejected applications and mark them as archived after import. -``` +The client uses `update_application` for stages, contacts, tags and follow-up dates, and `add_application_note` for notes. When `update_application` changes a list such as contacts or tags, the list it sends replaces the old one, so a careful client reads the application first and sends the full list. -## Update stages and notes - -Use `update_application` for structured changes and `add_application_note` when you want an activity timeline entry without changing other fields. - -```text -Move my Acme Senior Product Engineer application to interview and add a note: Recruiter screen scheduled for July 12 at 10:00. -``` - -```text -Add a note to the Globex application: Submitted take-home assignment and waiting for review. -``` +When an application with a linked resume reaches **Applied** or a later stage, Reactive Resume saves that resume, and the linked letter, as a sent version named after the company. You can see later exactly what you sent. ## Schedule interviews -Use `add_application_interview` to put an interview on an application. Each interview has a type (screening, technical, behavioral, onsite, or other), a start date-time, a duration, and an optional location and notes. Interviews show on the application timeline and on the Calendar view of the Applications page, and an application can have any number of them. Use `update_application_interview` to reschedule and `delete_application_timeline_entry` to cancel. - ```text -Schedule a 30-minute screening call with Acme for October 1 at 10:30 Eastern, on Zoom. +Schedule a 45-minute technical interview with Northwind Games on October 8 at 3 pm Pacific, on Google Meet. ``` ```text -Move my Globex technical interview to the following Tuesday at 2 pm and make it 90 minutes. +Move the Northwind Games interview to October 9 at the same time. ``` ```text -Set a follow-up date for the Stripe application to next Monday, with the note: Ask whether they need more portfolio examples. +Cancel the onsite interview with Blue Harbor Studio. +``` + +Interviews have a type (`screening`, `technical`, `behavioral`, `onsite` or `other`), a start time, a duration (60 minutes if you don't say), and an optional location and notes. They appear on the application's timeline and on the Applications calendar. The client uses `add_application_interview`, `update_application_interview` and, to cancel, `delete_application_timeline_entry`. See [Scheduling interviews](/guides/scheduling-interviews). + +## Attach what you sent + +```text +Attach this PDF as the resume I sent to Northwind Games. ``` ```text -Update the contacts on the Acme application. Recruiter: Priya Shah, priya@example.com. Hiring manager: Jordan Lee, LinkedIn URL https://www.linkedin.com/in/example. +Remove the cover-letter PDF from the Blue Harbor Studio application. ``` -```text -Archive every rejected application older than 30 days, but show me the list and ask for confirmation before applying the bulk update. -``` - -## Attach sent documents - -Use document attachment when you want the tracker to store the exact resume PDF or cover-letter PDF you sent for an application. - -```text -Attach this PDF as the sent resume for the Acme application, then confirm the application now has a resume document. -``` - -```text -Attach this cover letter PDF to the Globex Staff Engineer application. -``` - -```text -Replace the resume PDF on the Stripe application with this updated PDF. Add a note that I resent the revised resume. -``` - -```text -Remove the cover-letter PDF from the Acme application, but keep the application record and timeline. -``` - - - `attach_application_document` accepts base64-encoded PDF bytes and `contentType: "application/pdf"`. Some MCP clients hide that detail when they can read local files. If your client cannot read local files, upload the PDF from the web app instead. - - -## Run Application Copilot - -Application Copilot tools let an agent use the same AI workflows available in the application detail panel. - -```text -Score the resume linked to my Acme application against the saved job description. Summarize the biggest match gaps and do not change my resume. -``` - -```text -Create a tailored resume copy for the Globex Staff Engineer application. Keep the original resume unchanged, link the tailored copy back to the application, and tell me the new resume name. -``` - -```text -Draft a cover letter for the Stripe application using the linked resume and job description. Keep it concise and specific to the role. -``` - -```text -Draft a follow-up email for the recruiter on the Acme application. Mention that I enjoyed the technical screen and ask about next steps. Do not mark the email as sent. -``` - -```text -Review all interview-stage applications with linked resumes. For each one, score the match and list the top three tailoring opportunities. -``` +`attach_application_document` accepts one resume PDF and one cover-letter PDF per application, up to 10 MB each, sent as base64. Attaching a new file replaces the old one. - Review AI-generated resumes, cover letters, and messages before sending them. MCP tools can draft and save context, but you are responsible for the final content. + Anyone with the address of an attached PDF can download it without signing in. Attaching a file never sends it to an employer. -## Maintain your pipeline +If your client can't read files from your computer, attach the PDF in the app instead. See [Managing an application](/guides/managing-an-application). -Ask the agent to do periodic cleanup with explicit confirmation before broad changes. +## Use AI on an application + +These tools need a tested default AI provider. Scoring and tailoring also need a job description and a linked resume on the application. Drafting uses them when they're there. ```text -Find applications that have not changed in 21 days. Group them by stage and recommend which ones need a follow-up, archive, or no action. +Score my linked resume against the Northwind Games job description and list the biggest gaps. Don't change anything. ``` ```text -Add the tag needs-follow-up to every active application with a follow-up date before today. Show me the list before updating. +Make a tailored copy of my resume for the Northwind Games application. ``` ```text -Move all applications tagged offer to offer stage, unless they are already archived. +Draft a cover letter for the Northwind Games application. ``` ```text -Archive rejected applications older than 60 days. Ask for confirmation before making changes. +Draft a short follow-up to the recruiter at Northwind Games asking about next steps. +``` + +| Tool | What it changes | +| --- | --- | +| `score_application_match` | Saves a new match score on the application, replacing the previous one. | +| `tailor_resume_for_application` | Creates a private copy of the linked resume with a rewritten summary, links the copy to the application in place of the original and adds a timeline note. The original resume is unchanged. | +| `draft_application_message` | In cover-letter mode, saves a new cover letter and returns its text and ID. In follow-up mode, returns the text only. It never sends anything. | +| `autofill_application_from_job` | Returns suggested company, role, location and salary from a pasted posting. It saves nothing. | + +Read anything the AI writes before you use it. You can also tailor resumes and write letters from the app; see [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job). + +## Clean up + +```text +Find applications that haven't changed in three weeks and suggest which to follow up on and which to close. Don't change anything yet. ``` ```text -Delete these duplicate application records after confirming which one has the most complete timeline and documents. +Add the tag follow-up to every application in the applied stage with a follow-up date before today. Show me the list first. ``` -## Prompt library +`bulk_update_applications` moves up to 200 applications to a stage or adds tags to them. `delete_application` and `bulk_delete_applications` delete permanently, including uploaded PDFs that no other application uses; there is no Trash for applications. -Use these prompts as starting points. Replace company names, roles, tags, dates, and file references with your own details. +## Suggested client instructions -### Daily review +Add this to your client's project or session instructions so it handles your applications carefully: ```text -Give me a daily application tracker brief. Include applications needing follow-up today, interviews coming up, stale saved roles, and any applications missing a linked resume. -``` - -```text -Show my active applications in table form with company, role, stage, source, tags, follow-up date, and last updated time. -``` - -```text -Which applications are missing job descriptions, contacts, sent resume PDFs, or cover-letter PDFs? -``` - -### Research and capture - -```text -Create a saved application from this job post. Extract company, role, location, salary, source URL, and job description. Add tags for the main technologies mentioned. -``` - -```text -I am considering this role but have not applied. Add it as saved, link my backend resume, and add a note with the three reasons it looks relevant. -``` - -```text -Add this recruiter contact to the matching application and note that they reached out on LinkedIn today. -``` - -### Applying - -```text -Move the Acme application from saved to applied. Set applied date to today, attach the resume PDF I sent, and add a note with the application portal confirmation number. -``` - -```text -I just applied to three roles. Create applications for each one, tag them applied-today, and remind me to follow up in one week. -``` - -```text -Find the best resume to link to this application based on role title and tags. Ask me before updating the application. -``` - -### Interviews and follow-ups - -```text -Move the Globex application to screening and add a recruiter screen contact with the recruiter's name and email. -``` - -```text -Add a note that the onsite interview is scheduled for July 18. Set the follow-up date to July 19. -``` - -```text -Draft a short follow-up after my interview. Use the application's company, role, recruiter contact, and timeline notes. -``` - -### Reporting - -```text -Summarize my job search this month: number applied, interviews, offers, rejections, top sources, and response rate. -``` - -```text -Which sources are producing interviews? Compare LinkedIn, referrals, company sites, recruiters, and other sources. -``` - -```text -Show applications by stage and tell me where the pipeline is blocked. -``` - -## Suggested agent instruction - -Add this to your MCP client's project or session instructions when you want the agent to manage applications safely: - -```text -Use Reactive Resume MCP for application tracking. Start by calling list_applications before creating a new record so you do not duplicate existing applications. Confirm before delete_application, bulk_delete_applications, bulk_update_applications, replacing attached documents, or archiving more than five applications. Prefer add_application_note for timeline updates. Use Application Copilot tools only when the application has enough context, and never present AI-generated cover letters or follow-ups as sent messages. +Use Reactive Resume MCP for job applications. Call list_applications before creating an application so you don't duplicate one. Before update_application changes contacts or tags, read the application and send the complete list. Ask me before delete_application, bulk_delete_applications, bulk_update_applications, or replacing an attached document. Score or tailor only when the application has a job description, and never describe a drafted letter or follow-up as sent. ``` ## Troubleshooting -| Issue | What to do | +| Problem | What to do | | --- | --- | -| The agent cannot see application tools | Reconnect the MCP server and ask the client to refresh tool discovery. Confirm you are connected to Reactive Resume v5.2.2 or later. | -| The agent creates duplicates | Ask it to run `list_applications` first and match by company, role, and source URL before creating records. | -| Document attachment fails | Attach only PDFs. If your MCP client cannot read local files, upload the document from the web app. | -| Match scoring or tailoring fails | Link a Reactive Resume to the application and add a job description. Confirm your AI provider is configured. | -| A bulk action changed too much | Use the application list and timeline to inspect what changed. For future sessions, require confirmation before bulk actions. | +| The client can't see application tools | Reconnect the server and ask the client to refresh its tools. | +| The client creates duplicates | Ask it to list applications first and match on company and role before creating one. | +| A stage change to `rejected` or an archive request fails | Those no longer exist. Use the `closed` stage with a reason. | +| Attaching a document fails | Only PDFs up to 10 MB are accepted. Attach from the app if your client can't read the file. | +| Scoring, tailoring or drafting fails | Check that a default AI provider is set up and tested. For scoring and tailoring, also check that the application has a job description and a linked resume. | + +## Related guides + +- [Using the MCP server](/guides/using-the-mcp-server): connecting a client, and the full tool list. +- [Tracking job applications](/guides/tracking-job-applications): the Applications views in the app. +- [Managing an application](/guides/managing-an-application): the application detail sheet. diff --git a/docs/guides/managing-documents.mdx b/docs/guides/managing-documents.mdx new file mode 100644 index 000000000..133a6002e --- /dev/null +++ b/docs/guides/managing-documents.mdx @@ -0,0 +1,139 @@ +--- +title: "Managing your documents" +description: "Find, sort, rename, duplicate, lock and remove your resumes and cover letters from the Documents page in Reactive Resume." +--- + +**Documents** is the page you see after signing in. It holds every resume and cover letter in your account, so you can keep one version per role, company or language and find the right one fast. + + + The Documents page with All, Resumes and Letters tabs, a search field, a sort menu, grid and list buttons, tag chips, and four document cards + + +Each card shows the document's first page, its name, its type and when you last edited it. A card made on this device that you haven't opened yet has a **New** badge, and a locked document shows a lock icon. If a document is linked to a job application, the company name appears under it. + +## Open a document + +Select a card (or a name in list view) to open it. Resumes open in the resume editor and cover letters in the letter editor. You can also open the card's **⋯** menu and choose **Open**. + +## Find a document + +Use the controls above your documents: + + + The Documents toolbar with All 4, Resumes 3 and Letters 1 tabs, a Search field showing the slash shortcut, a Last edited sort menu, grid and list buttons, and tag chips below + + +- **All**, **Resumes** and **Letters** show one type at a time. Each tab shows how many documents it holds. +- **Search** matches document names, tags, and the company or role of a linked application. Press / anywhere on the page to jump to it. +- The sort menu orders documents by **Last edited** (the default), **Name** or **Created**. +- Tag chips appear once any document has a tag. See [Organizing documents with tags](/guides/organizing-with-tags). + +If nothing matches, select **Clear search and filters** to see everything again. + + + The page address keeps your tab, search, tags and sort, so you can bookmark a filtered view. To search across documents and applications from anywhere in the app, use the [command bar](/guides/using-the-command-bar). + + +## Switch between grid and list + +Use the two buttons at the right of the toolbar. The grid shows page previews. The list is denser, with **Name**, **Type**, **Application** and **Edited** columns. Reactive Resume remembers your choice on this device. + + + Documents in list view with columns for Name, Type, Application and Edited, and a menu button at the end of each row + + +## Use the document menu + +Every card and row has a **⋯** menu. You can also right-click a card or row (or press and hold on a touch screen) to open the same menu. + + + The options menu for Game Developer Resume with Open, Rename, Duplicate, Copy for a job, Tags, Lock editing and Move to Trash + + +| Item | What it does | +| --- | --- | +| **Open** | Opens the document in its editor. | +| **Rename** | Lets you edit the name in place. | +| **Duplicate** | Makes a copy named "… (copy)" with the same tags. | +| **Copy for a job…** | Resumes only. Makes a copy linked to a job application. | +| **Link to application…** | Letters only. Links the letter to one of your open applications, or unlinks it. | +| **Tags…** | Adds or removes tags. | +| **Lock editing** / **Unlock** | Protects the document from changes, or allows them again. | +| **Move to Trash** | Moves the document to Trash, where you can restore it for 30 days. | + +## Rename a document + + + + Open the document's **⋯** menu and choose **Rename**. The name turns into a text field with the current name selected. + + + A document card with its name field in edit mode showing Unity Developer Resume + + + + Names can be up to 100 characters. + + + Press Enter or click elsewhere to save. Press Esc to keep the old name. + + + +A new blank resume takes its name from its headline until you rename it. After you rename it, the name stays as you typed it. + +## Duplicate a document + +Choose **Duplicate** to make a separate copy. The copy is named "*name* (copy)" and keeps the original's tags. The copy appears at the top of the list with a **New** badge. Editing the copy never changes the original. + +Duplicating is useful when you want a short and a long version, a version in another language, or a safe place to try a new template. + +## Copy a resume for a job + +**Copy for a job…** opens the **Copy a resume for a job** dialog with that resume selected under **Start from**. Pick an application under **For which job?** (or **No job yet**), check the suggested **Name**, and select **Create and open**. + + + The Copy a resume for a job dialog with a list of resumes to start from, a No job yet chip, a Name field and a Create and open button + + +When you pick an application, the copy is linked to it, Check and the Assistant use that job's posting, and the copy opens with the Assistant ready to tailor it. For the full workflow, see [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job). + +## Lock a document + +Choose **Lock editing** to protect a document you've already sent, so it can't change by accident. While a document is locked: + +- its card shows a lock icon; +- **Rename**, **Tags…**, **Link to application…** and **Move to Trash** are unavailable; +- the editor shows it read-only. In the resume editor a note reads "Locked. Unlock to edit." with an **Unlock** button. + +You can still open, duplicate and download it. Choose **Unlock** in the same menu to edit it again. + + + A resume card with a lock icon and its menu showing Rename, Tags and Move to Trash unavailable and an Unlock item + + +## Remove a document + +Choose **Move to Trash**. The document leaves Documents right away and a message appears with **Undo**. Documents stay in Trash for 30 days before they're deleted for good. See [Using the Trash](/guides/using-the-trash). + +## Create a new document + +Select **New** in the sidebar, or press N anywhere outside a text field, to open the **New document** dialog. + + + The New document dialog with Import a resume, Copy a resume for a job, Start blank, New cover letter instead and Try with a sample resume + + +- **Import a resume**: build a resume from a PDF, Word, JSON or LinkedIn file. See [Importing a resume](/guides/importing-resumes). +- **Copy a resume for a job**: copy one of your resumes, as described above. +- **Start blank**: create an empty resume and open it. See [Creating your first resume](/guides/creating-your-first-resume). +- **New cover letter instead**: create a letter. It takes your details and design from the resume you edited most recently. See [Writing a cover letter](/guides/writing-a-cover-letter). +- **Try with a sample resume**: create a filled-in example resume to explore. + +You can also drop a file anywhere on the Documents page to import it. + +## Related guides + +- [Organizing documents with tags](/guides/organizing-with-tags): group documents and filter by tag. +- [Using the Trash](/guides/using-the-trash): restore a document or delete it for good. +- [Importing a resume](/guides/importing-resumes): bring in a resume you already have. +- [Sharing your resume publicly](/guides/sharing-your-resume-publicly): send a link instead of a file. diff --git a/docs/guides/managing-resumes-from-the-dashboard.mdx b/docs/guides/managing-resumes-from-the-dashboard.mdx deleted file mode 100644 index 6749be5ba..000000000 --- a/docs/guides/managing-resumes-from-the-dashboard.mdx +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: "Managing resumes from the dashboard" -description: "Search, sort, filter, open, duplicate, lock, update, and delete resumes from the Reactive Resume Resumes dashboard in grid or list view." ---- - -The **Resumes** dashboard is where you manage every resume in your account. Use it to keep separate versions for different roles, clients, locations, or application stages. - - - Resumes dashboard showing sort controls, grid view, create and import cards, and a sample resume - - -## Search your resumes - -Once you have more than a few resumes, a **Search** field appears above the list. Type any part of a resume name to filter the dashboard in place. - -You can also open the command palette from anywhere in the app with `Cmd/Ctrl+K`, or by clicking **Search** in the dashboard sidebar. From there you can jump to a resume, application, or agent thread by name, or run quick actions like **New Application** and **New Thread**. Select **Resumes**, **Applications**, or **Threads** to filter results to a single entity type. - -## Choose a view - -The dashboard supports two views: - -- **Grid** shows each resume as a card. This is useful when you want a visual overview. -- **List** shows resumes in rows. This is useful when you have many resumes and want a denser view. - -Use the **Grid** and **List** tabs in the top-right of the dashboard to switch between them. - - - Resumes dashboard showing the same resume in list view - - -## Sort resumes - -Use the **Sort by** menu to change the order of your resumes. - -| Sort option | When to use it | -| --- | --- | -| **Last Updated** | Find the resume you worked on most recently. | -| **Created** | Review resumes by when they were first created. | -| **Name** | Keep resumes in alphabetical order. | - -## Filter by tags - -If you add tags to your resumes, the dashboard shows a **Filter by** menu. Select one or more tags to show only matching resumes. - -Tags are useful for grouping resumes by: - -- target role, such as `frontend` or `product`; -- application status, such as `draft` or `sent`; -- market, region, client, or company name. - - - You can add or change tags when creating, updating, or duplicating a resume. - - -## Open a resume - -Click a resume card or row to open it in the builder. - -You can also open the resume menu and choose **Open**. - - - After you create or import a resume, Reactive Resume opens it directly in the builder instead of returning you to the - dashboard. - - -## Update name, slug, and tags - -Use **Update** when you want to change a resume's metadata. - - - - On the dashboard, open the menu for the resume you want to edit. - - - - Select **Update** to open the resume details dialog. - - - - Change the **Name**, **Slug**, or **Tags**. - - - - Click **Save Changes**. - - - - - The slug is part of the public URL. If a resume is public, changing the slug changes the link people use to view it. - - -## Duplicate a resume - -Use **Duplicate** when you want to create a new version without changing the original. - -The duplicate dialog starts with the same tags, a copied name, and a copied slug. Edit these before saving if you want the new version to be easier to identify. - -Good reasons to duplicate a resume: - -- tailoring one resume for a specific job posting; -- keeping a short and long version; -- testing a new template or layout; -- creating a localized version. - -## Lock or unlock a resume - -Use **Lock** to prevent accidental edits or deletion. A locked resume cannot be updated or deleted until you unlock it. - -To edit a locked resume later, open the resume menu and choose **Unlock**. - -## Delete a resume - -Use **Delete** only when you no longer need the resume. - - - Deleting a resume cannot be undone. If you might need the content later, export a JSON backup first. See - [Exporting your resume](/guides/exporting-your-resume). - diff --git a/docs/guides/managing-sections.mdx b/docs/guides/managing-sections.mdx new file mode 100644 index 000000000..68c8d38ea --- /dev/null +++ b/docs/guides/managing-sections.mdx @@ -0,0 +1,127 @@ +--- +title: "Managing sections" +description: "Add, hide, rename, reorder and remove resume sections in Write mode, create custom sections, and control how each section prints." +--- + +A section is one block of your resume, such as Experience, Education or Skills. In **Write** mode you add the sections you need, put them in the order you want them printed, and choose how each one looks on the page. This guide covers everything you can do with a whole section. To work with the entries inside a section, see [Editing entries](/guides/editing-entries). + +## Where your sections live + +Open a resume and select **Write** in the editor bar. Below the Basics card is the **Sections · print order** list. It shows every section that has content, in the order it prints: page by page, and within each page the main column first, then the sidebar. + + + The Sections list in Write mode, showing Page 1 with Summary, Education and Experience, a Sidebar divider above Profiles and Skills, and Page 2 with Experience and Awards. Each row has a drag handle, an entry count, an eye icon, a chevron and a three-dot menu. + + +Each row has, from left to right: + +- A **drag handle** to reorder the section. +- The section **title**. Select it, or the chevron, to open the section and see its entries. +- The number of **entries** in the section. +- The **eye** icon, which hides or shows the section on the page. +- The **⋯** menu with the rest of the section's options. + +Dividers mark where a new page starts (**Page 2**, **Page 3**) and, on templates with two columns, where the **Sidebar** begins. Built-in sections without any content aren't listed; they wait in **Add section** until you need them. + +## Add a section + + + + Scroll to the bottom of the sections list and select **Add section**. The menu lists every built-in section you aren't using yet. + + On a brand-new blank resume you also see shortcuts for the sections most resumes have: **Experience**, **Education**, **Skills** and **Summary**. Select one to add it straight away. + + + The empty sections list on a blank resume, with buttons for Experience, Education, Skills and Summary, an Import it link, and the Add section button below. + + + + Select the section you want. It appears in its usual place in the layout (Skills, for example, goes in the sidebar on two-column templates) with one empty entry, already open and ready to type into. The Summary opens as a single text box instead. + + + +A new entry stays a **Draft** until you fill in its main field, such as the company for a job or the school for a degree. Drafts are saved but don't print. See [Editing entries](/guides/editing-entries) for the fields each section has. + +## Create a custom section + +Use a custom section when none of the built-in names fits, or when you want a second section of the same kind, such as "Earlier Experience" or "Teaching". + + + + Select **Add section**, then **Custom section**. The menu asks **What goes in it?** + + + Choose the type whose fields you want: Experience, Education, Projects, Skills, Languages, Interests, Awards, Certifications, Publications, Volunteer, References, Profiles, or Summary for a block of free text. + + + The new section is added at the end of your last page and takes the type's name. Open its **⋯** menu and select **Rename…** to give it your own title. + + + + + The Add section menu listing built-in sections in two columns, and the Custom section submenu open beside it under the heading What goes in it?, listing thirteen section types. + + + + Once every built-in section is in use, **Add section** lists the custom section types directly. + + +## Hide or show a section + +Select the **eye** icon on a section's row. A hidden section's title is struck through in the list and it disappears from the page, but its content stays in the resume. Hidden sections aren't printed, downloaded or shared. Select the eye again to bring the section back. + +To hide a single entry instead of the whole section, use **Hide from page** in the entry's menu. See [Editing entries](/guides/editing-entries#hide-an-entry-from-the-page). + +## Reorder sections + +The list order is the print order, so moving a row moves the section on the page. You can: + +- **Drag** a row by its handle. +- Select the section's title and press ⌥ Option + ↑ or ↓ (Alt + ↑ or ↓ on Windows and Linux). +- Open the **⋯** menu and select **Move up** or **Move down**. + +Moving a section past a divider moves it onto that page or column. For example, dragging a section below the **Sidebar** divider puts it in the sidebar. To change the columns, sidebar width or number of pages themselves, see [Arranging the layout](/guides/arranging-the-layout). + +## Section options + +Open a section's **⋯** menu to see everything else you can do with it. + + + The options menu for the Skills section, listing Add entry, Move up, Move down, Rename…, Icon…, Show heading (checked), Columns, Keyword layout, Keep on one page, Start on a new page and Clear section. + + +| Option | What it does | +| --- | --- | +| **Add entry** | Adds an empty entry at the end of the section. Not shown for the built-in Summary. | +| **Sort by date** | Experience and Education only. Puts current entries first, then the rest from the most recent end date back. Entries without a start date, or whose end comes before the start, stay at the end, and a message names them. | +| **Move up** / **Move down** | Moves the section one place in the print order. | +| **Rename…** | Changes the title printed above the section. Leave the name empty to go back to the original title. | +| **Icon…** | Picks the icon shown next to the section title, on templates that show section icons. | +| **Show heading** | Clear it to print the section without its title. | +| **Columns** | Splits the section's entries into 1 to 6 columns. Skills also offers **1 column, inline**, which runs the skills together on one line. | +| **Keyword layout** | Skills only. Prints each skill's keywords **Inline** or as a **Bulleted list**. | +| **Keep on one page** | Stops the section from being split across two pages. | +| **Start on a new page** | Always starts the section at the top of a new page. | +| **Clear section** | Built-in sections: removes all entries (or the Summary text). | +| **Delete section** | Custom sections: removes the section and its entries. | + + + A dialog titled What do you want to rename this section to?, with the hint Leave empty to reset the title to the original, the name Earlier Experience typed in, and Cancel and Confirm buttons. + + +## Remove a section + +Open the section's **⋯** menu and select **Clear section** (built-in sections) or **Delete section** (custom sections). A message confirms the change with an **Undo** button, in case you didn't mean it. A cleared built-in section leaves the list and goes back into **Add section**, so you can add it again later. + + + If you only want a section off the page for now, hide it with the eye icon instead. Hiding keeps everything you wrote. + + +You can also undo any section change with ⌘ Z (Ctrl Z on Windows and Linux) while no text field is focused, or go back further from **History**. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). + +## Related guides + +- [Editing entries](/guides/editing-entries): add, duplicate, move and hide the entries inside a section. +- [Entering dates](/guides/entering-dates): how dates are typed, checked and printed. +- [Arranging the layout](/guides/arranging-the-layout): columns, sidebar, pages, and moving sections between pages. +- [Fitting content on a page](/guides/fitting-content-on-a-page): what to do when your resume runs long. diff --git a/docs/guides/moving-items-between-sections.mdx b/docs/guides/moving-items-between-sections.mdx deleted file mode 100644 index f23edeb7a..000000000 --- a/docs/guides/moving-items-between-sections.mdx +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: "Moving items between sections" -description: "Move resume items between sections or across pages in the Reactive Resume builder to reorganize content and split lengthy sections cleanly." ---- - -If you have a long work history or a long list of projects, you may want to split items across multiple pages or reorganize them into different sections. The **Move to** feature relocates any item to another section or page. - -## Why move items? - -- Split long sections. If your Experience section spans more than one page, move older roles to a custom section on page 2. -- Reorganize content. Move a project from "Projects" to a custom "Open Source" section, or a skill to a different grouping. -- Control the page layout. Choose exactly which items appear on which page. - -## How to move an item - - - - Navigate to your resume and open it in the builder. - - - Make sure you have at least one item in a section (e.g., an experience entry, project, or skill) before proceeding. - - - - - - In the left sidebar, find the section containing the item you want to relocate. Click on the section to expand it and view all items. - - - Screenshot of the left sidebar with an expanded section containing multiple items - - - - - - Each item has a **dropdown menu** (three-dot icon or chevron) on the right side. Click this icon to reveal the available actions. - - - Screenshot of the dropdown menu icon on a section item - - - - - - In the dropdown menu, hover over or click the Move to option. This will open a submenu showing all - available destinations. - - - - The submenu displays available destinations organized by: - - - **Existing sections** of the same type (e.g., other Experience sections) - - **Pages** where you can place the item - - **Custom sections** if any exist - - Click on your desired destination to move the item there. - - - Screenshot of the 'Move to' submenu with destination options - - - - If a custom section of the same type doesn't exist on your target page, Reactive Resume creates one for you, so you can split a section across pages without setting it up first. - - - - - - After selecting a destination, the item will be moved immediately. You can verify by: - - - Checking the destination section in the left sidebar - - Looking at the resume preview to see where the item now appears - - - Screenshot of the item in its new location - - - - - -## Example: splitting work experience across pages - -A common case is a work history that is too long to fit on a single page. - - - - Review your Experience section and decide which roles should appear on page 1 (typically your most recent and relevant positions) and which can go on page 2. - - - - For each older position you want to relocate: - - 1. Open the item's dropdown menu - 2. Click Move to - 3. Select **Page 2** (or the appropriate page) - - - If no Experience section exists on page 2, a new custom section will be created automatically with the same type, so your formatting stays consistent. - - - - - - Check the resume preview to ensure: - - - Page 1 contains your most important, recent roles - - Page 2 continues with your earlier experience - - The section headings and styling remain consistent - - - - -## Tips for organizing multi-page resumes - - - Keep chronological order within each page, and move complete job entries rather than splitting a single role across - pages. - - - - After moving items to a new custom section, rename it (e.g., "Earlier Experience" or "Additional Projects") so - recruiters know what they are looking at. - - - - Moving items reorganizes your content but doesn't change the data itself. You can always move items back, or to a - different section. - - -## Troubleshooting - -### I don't see the "Move to" option - -Make sure you're clicking the dropdown menu on a **section item** (like an individual job or project), not the section header itself. The Move to feature is only available for items within sections. - -### The destination I want isn't listed - -The Move to submenu shows destinations compatible with the item type. For example, an Experience item can only be moved to other Experience-type sections. If you need to change an item's type entirely, recreate it in the section you want. - -### My custom section wasn't created - -If you're moving to a page that already has a section of the same type, the item will be added to that existing section rather than creating a new one. This is by design to avoid duplicate sections. diff --git a/docs/guides/organizing-with-tags.mdx b/docs/guides/organizing-with-tags.mdx new file mode 100644 index 000000000..24069fb20 --- /dev/null +++ b/docs/guides/organizing-with-tags.mdx @@ -0,0 +1,55 @@ +--- +title: "Organizing documents with tags" +description: "Add tags to your resumes and cover letters in Reactive Resume, then filter the Documents page by one or more tags to find the right version." +--- + +Tags are short labels you add to documents, such as the kind of role, the industry, or whether you've sent it. Once any document has a tag, Documents shows a row of tag chips you can use as filters. + +## Add or remove tags + + + + On Documents, open the document's **⋯** menu and choose **Tags…**. + + + Type in the **Add a keyword...** field and press Enter or type a comma , to add it. Add as many as you like. + + + The Tags dialog with games and unity tags added and an empty Add a keyword field + + + + Select the pencil next to a tag to change it, or the **×** to remove it. You can drag tags to reorder them. + + + Select **Save**. Closing the dialog without saving discards your changes. + + + +Tags are unavailable while a document is locked. Unlock it first (see [Managing your documents](/guides/managing-documents#lock-a-document)). + + + Duplicating a resume, or copying it for a job, keeps its tags, so a family of versions stays grouped. + + +## Filter by tag + +Select a chip above your documents, for example **#unity**, to show only documents with that tag. The chip turns green while it's active. Select more chips to narrow the list further: a document must have every selected tag to appear. Select an active chip again to turn it off. + + + The tag chips #games, #senior and #unity with #unity active, and a single matching resume card below + + +Tag filters work together with the **All**, **Resumes** and **Letters** tabs and with search. The search field also matches tag names, so typing a tag finds its documents too. + +## Ideas for tags + +- the kind of role: `frontend`, `product`, `teaching`; +- the stage: `draft`, `sent`; +- the market or language: `uk`, `german`; +- a version: `one-page`, `long`. + +## Related guides + +- [Managing your documents](/guides/managing-documents): search, sort, rename and duplicate. +- [Tracking job applications](/guides/tracking-job-applications): follow each application from saved to closed. diff --git a/docs/guides/scheduling-interviews.mdx b/docs/guides/scheduling-interviews.mdx new file mode 100644 index 000000000..b8bed6093 --- /dev/null +++ b/docs/guides/scheduling-interviews.mdx @@ -0,0 +1,122 @@ +--- +title: "Scheduling interviews" +description: "Add interviews and follow-up reminders to your applications, see them on the Calendar view, and add them to your own calendar with an .ics file." +--- + +Add each interview to its application so it shows as the application's next step, in its history and on the Calendar view. You can also set a follow-up reminder, and send any dated step to the calendar app you already use. + +## Schedule an interview + +You can start from the application or from the calendar. + + + + + + Select it in **Applications** to open its details. + + + Under **Next step**, select **Edit**, then **Schedule an interview…**. + + + + + Next step card with the Edit menu open, listing Edit this interview…, Schedule an interview… and Set a follow-up… + + + + + + In **Applications**, select the **Calendar** tab. + + + Select **Schedule interview**, or hover over a day and select **+** to start on that day. + + + Under **Application**, pick the application the interview belongs to. Closed applications aren't listed. + + + + + +Then fill in the interview: + + + + Choose **Screening**, **Technical**, **Behavioral**, **Onsite** or **Other**. Each type has its own color on the calendar. + + + **Date & time** is required. It starts at 9:00 the next day, or on the day you picked in the calendar (the next full hour if that day is today), in your own time zone. + + + Pick a **Duration** from 15 minutes to 4 hours. The default is 1 hour. + + + **Where** can be a video link, an office address or a phone number. Use **Notes** for who you're meeting and what to prepare. + + + Select **Schedule**. + + + + + Schedule an interview dialog for Senior Gameplay Programmer at Northwind Games, with Behavioral selected as the interview type, a date and time, a 1 hr duration, Where set to a video call, notes, and Cancel and Schedule buttons + + +The interview appears in the application's **Activity**, on the calendar, and as the **Next step** until it has finished. Scheduling an interview doesn't change the application's stage, so move it to **Interview** yourself if you want to. + +## Edit or delete an interview + +Select the interview on the calendar, in **Upcoming interviews**, or in the application's **Activity** (or use **Edit** > **Edit this interview…** when it's the next step). Change what you need and select **Save changes**. To remove it, select **Delete** and confirm. + +## Use the Calendar view + +The **Calendar** tab shows one month of interviews across all your applications. + +- Use the arrows to change month, and **This month** to come back to today. +- Each day shows up to three interviews, with the time and company. Select **+*n* more** to see the rest of a busy day. +- **Upcoming interviews**, on the right, lists every interview that hasn't finished yet, grouped by day. Select a card to edit the interview, or **View application** to open the application. +- The legend under the calendar shows the color of each interview type. + + + Calendar view for September 2026 with interview chips on several days, the Schedule interview button, and an Upcoming interviews list with cards for Northwind Games, Brightline Studios and Redwood Arcade + + +The calendar follows the page's search and **Show closed** settings, so interviews for closed applications are hidden unless **Show closed** is on. The Calendar view isn't available on phones; use each application's **Next step** instead. + +## Set a follow-up reminder + +A follow-up is a reminder to check in, for example to email the recruiter a week after applying. + + + + In the application's details, under **Next step**, select **Edit**, then **Set a follow-up…** (or **Change the follow-up…**). + + + Choose a **Date** and, if you like, fill in **What to do**, such as "Email the recruiter". When you change an existing follow-up, the fields start empty, so enter the date and note again. + + + Select **Save**. To remove the reminder, select **Clear**. + + + + + Follow-up dialog with Date set to 03.10.2026, What to do set to Email the recruiter, and Clear and Save buttons + + +The follow-up shows as the next step (unless an interview is coming up first) and turns amber once the date has passed. You can also set the follow-up date and note in **Edit details…**. + +## Add a step to your calendar + +When the next step is an interview or a follow-up, select **Add to calendar** under it. Reactive Resume downloads an `.ics` file named after the company and date. Open it to add the event to Apple Calendar, Google Calendar, Outlook or any other calendar app. + +The event includes the type of step, the role and company, and the start and end time. Interviews also include where and your notes. Follow-ups are added as 30-minute events at 9:00 on the chosen day. + + + The `.ics` file is a one-time copy. If you change the interview in Reactive Resume later, download it again and update the event in your calendar. + + +## Related guides + +- [Managing an application](/guides/managing-an-application): the rest of the details sheet. +- [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job): use **Prepare for next step** to get ready for an interview. diff --git a/docs/guides/selecting-page-format.mdx b/docs/guides/selecting-page-format.mdx index 825cd394f..5ffee39ff 100644 --- a/docs/guides/selecting-page-format.mdx +++ b/docs/guides/selecting-page-format.mdx @@ -1,160 +1,91 @@ --- -title: "Selecting the right page format" -description: "Compare the A4, Letter, and Free-Form page format options in Reactive Resume and pick the right size, margins, and orientation for your resume." +title: "Choosing paper size, margins and language" +description: "Set your resume to Letter, A4 or free-form, choose margins, set the language for section titles and dates, and toggle icons and link underlines." --- -Reactive Resume has three page formats: **A4**, **Letter**, and **Free-Form**. The format you pick changes how your resume is rendered and exported as a PDF. +The **Page** group in Design sets up the paper your resume is printed on: its size, its margins, the language of the words Reactive Resume adds for you, and whether contact details show icons and underlined links. -## Available formats +## Open the Page group -### A4 +In the editor, select **Design** (or press 2), then select **Page** in the row of links at the top of the panel. -A4 is the international standard paper size used in most countries outside North America. When you select A4, your resume pages conform to these dimensions: + + The Page group with Paper set to A4 (the other option is Letter), Language set to English with the hint Changes section titles and date words only, not your content, Margins set to Normal, and the Icons in contact line and Underline links switches both on + -| Property | Value | -| -------- | -------------- | -| Width | 210mm (794px) | -| Height | 297mm (1123px) | +## Choose the paper size -Choose A4 if you're applying to jobs internationally or in regions that use the metric system. +Under **Paper**, select **Letter** or **A4**. -### Letter +| Paper | Size | Use it when | +| --- | --- | --- | +| **Letter** | 8.5 × 11 in (216 × 279 mm) | You're applying in the United States or Canada. | +| **A4** | 210 × 297 mm | You're applying almost anywhere else. | -Letter is the standard paper size in the United States and Canada. When you select Letter, your resume pages conform to these dimensions: +Your content keeps its place when you switch. Because Letter is shorter and wider than A4, entries can move between pages, so check the page breaks afterwards. -| Property | Value | -| -------- | -------------- | -| Width | 216mm (816px) | -| Height | 279mm (1056px) | +### Free-form: one long page -Choose Letter if you're applying to jobs in North America. +Free-form is for resumes that are only read on screen. It keeps the A4 width, but each page grows as tall as its content needs, so nothing ever runs onto an extra page. Each page is at least as tall as an A4 page. -### Free-Form - -Free-Form is built for resumes that are only read on a screen. Instead of matching a physical page size, it produces a **single continuous page** with no height limit. The width matches A4 (210mm), and the height extends to fit all your content. - -| Property | Value | -| -------- | ------------- | -| Width | 210mm (794px) | -| Height | Unlimited | - - - With Free-Form, there are no page breaks. Your resume renders as one continuous document, no matter how much content - you have. - - -## Why Free-Form exists - -Most resumes are never printed. Yours is almost always read digitally: on a screen, in an applicant tracking system (ATS), or by an AI screening tool. - -When your resume is processed digitally: - -- ATS parsers extract the text regardless of page dimensions -- AI scanners analyze the full document as a single unit -- Recruiters scroll through PDFs on their screens rather than printing them -- PDF parsing tools read the whole file regardless of page height - -Since physical page limits no longer apply in those cases, Free-Form lets you focus on the content instead of fitting it into a fixed page height. - - - If you don't plan on printing your resume, Free-Form is usually the simplest choice. Nothing overflows, and there are - no awkward page breaks. - - -## How to change your page format - - - - Navigate to your Dashboard and click on the resume you want to edit. - - - - The sidebar sits on the right edge of the resume builder. - - - - In the right sidebar, find and click on the **Page** section to expand it. - - - - Find the **Format** dropdown and select your preferred option: A4, Letter, or Free-Form. - - - Screenshot of the Format dropdown in the Page section - - - - - - Your resume preview updates immediately to reflect the new format. Check that your content displays correctly. - - - -## Choosing the right format - -| Situation | Recommended Format | -| --------------------------------------- | ------------------ | -| Applying to jobs in North America | Letter | -| Applying to jobs internationally | A4 | -| Digital-only applications (no printing) | Free-Form | -| Uploading to ATS or job portals | Free-Form | -| Need to print physical copies | A4 or Letter | -| Long resume with lots of content | Free-Form | -| Traditional industries (law, finance) | A4 or Letter | +To use it, open **Advanced** at the bottom of the Design panel and set **Page → Format** to **Free-form**. While a resume uses free-form, the **Paper** control in the Page group shows a third option, **Free-form**. Selecting **Letter** or **A4** there switches back, and the option disappears again. - If you switch from Free-Form to A4 or Letter, your content is split across multiple pages. Review the result and - check that the page breaks don't fall in awkward places. + A free-form resume prints poorly, because printers cut it into paper-sized pieces wherever it happens to break. Use Letter or A4 if anyone might print it, or if a job asks for a one- or two-page resume. -## Example PDFs +## Set the language -Download these sample resumes to see how different formats affect the final PDF output: +Choose a language under **Language**. It changes the words Reactive Resume writes for you, such as the default section titles ("Experience", "Education") and date words (month names, "Present"). It doesn't translate what you wrote. - - - A sample resume in A4 format showing traditional page breaks and constraints. - - - A sample resume in Free-Form format showing a continuous single-page layout. - - +The language also: -## Frequently asked questions +- sets the rules **Hyphenation** uses, if you turn it on in **Advanced → Typography**; +- mirrors the layout for right-to-left languages such as Arabic and Hebrew, so a left sidebar moves to the right. - - - Yes. An ATS parses the text in your PDF, not the page dimensions. Free-Form resumes work with applicant tracking systems. - +To show dates as "Mar 2022", "March 2022", "03/2022" or "2022-03", use **Date format** in **Advanced**. See [Entering dates](/guides/entering-dates). - - Yes. You can change the format at any time from the Page section in the right sidebar. Your content is preserved. - Only the layout changes. - +## Choose margins - - If the employer asks for a one-page resume and expects a traditional format, use A4 or Letter and fit your content on - a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips. - +Under **Margins**, select **Narrow**, **Normal** or **Wide**. Margins are the white space around the page edges. They also set the gap between a template's columns, below the header and between sections. - - Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but - the difference is negligible for typical resume lengths. - +| Margins | Left and right | Top and bottom | +| --- | --- | --- | +| **Narrow** | 10 pt | 8 pt | +| **Normal** | 14 pt | 12 pt | +| **Wide** | 19 pt | 16 pt | - - LinkedIn doesn't display uploaded resumes in their original format; it extracts the content. Free-Form works fine for LinkedIn uploads. - - +If you type exact margins in Advanced that don't match one of these, none of the three is selected. + +## Show or hide icons and link underlines + +- **Icons in contact line** shows small icons next to your email, phone, location and links. It also controls the icons on skills, profiles and interests. +- **Underline links** underlines your website and other links on the page. + +## Exact page settings + +**Advanced → Page** holds the exact values behind the Page group, plus a few extras: + + + The Advanced Page section with Format set to A4, Margin (Horizontal) 14 pt, Margin (Vertical) 12 pt, Spacing (Horizontal) 12 pt, Spacing (Vertical) 6 pt, and switches for Hide Link Underline, Hide Icons and Hide Section Icons + + +| Setting | What it does | +| --- | --- | +| **Format** | **A4**, **Letter** or **Free-form**. | +| **Margin (Horizontal)** / **Margin (Vertical)** | Exact margins, from 0 to 100 pt. | +| **Spacing (Horizontal)** | Space between items that sit side by side, for example in a section shown in several columns. | +| **Spacing (Vertical)** | Space in and between entries. Density in the Type group sets this too. The space between sections comes from **Margin (Vertical)**. | +| **Hide Link Underline** | The same as turning off **Underline links**. | +| **Hide Icons** | The same as turning off **Icons in contact line**. | +| **Hide Section Icons** | Hides the small icon before each section heading. You choose each section's icon from its **⋯** menu in Write, with **Icon…**. | + + + Cover letters have the same Page controls in their own Design mode. See [Writing a cover letter](/guides/writing-a-cover-letter). + + +## Related guides + +- [Fitting content on a page](/guides/fitting-content-on-a-page): use margins and other settings to stay within your pages. +- [Arranging the layout](/guides/arranging-the-layout): columns, sidebar and multi-page layouts. +- [Exporting your resume](/guides/exporting-your-resume): download the PDF in the paper size you chose. diff --git a/docs/guides/setting-up-passkeys.mdx b/docs/guides/setting-up-passkeys.mdx index 858d69620..23e64ab24 100644 --- a/docs/guides/setting-up-passkeys.mdx +++ b/docs/guides/setting-up-passkeys.mdx @@ -1,44 +1,53 @@ --- title: "Setting up passkeys" -description: "Register WebAuthn passkeys on your Reactive Resume account to sign in with biometrics, a device PIN, or a security key instead of a password." +description: "Add a passkey to your Reactive Resume account to sign in with your fingerprint, face, device PIN or security key instead of a password." --- +A passkey lets you sign in with the same check you use to unlock your device: a fingerprint, your face, a PIN or a hardware security key. There is no password to type or leak, and a passkey only works on the real Reactive Resume site, so it resists phishing. + +## Before you start + +You need a device or password manager that supports passkeys. Recent versions of macOS, iOS, Windows, Android, Chrome, Safari, Edge and Firefox all do, as do password managers such as 1Password and Bitwarden. + +## Add a passkey + - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. + + Select your name at the bottom of the sidebar, then **Settings**. On the **Account** page, find **Passkeys** under **Sign-in & security**. - - Open Settings from your avatar and choose Account. Everything about signing in is under **Sign-in & security**. - - - - In the Passkeys section, click the Register New Device button. - - - - Enter a descriptive name so you can recognize it later (for example, "MacBook Touch ID" or "iPhone Face ID"). - - - - Your browser or device shows a passkey prompt (WebAuthn). What it asks for depends on the authenticator you are registering: a biometric (Face ID / Touch ID / fingerprint), your device PIN, or inserting and touching a security key. - - - Passkeys are tied to your device (or password manager) and are a more secure alternative to passwords. - - + + Your browser or device asks you to create a passkey. Follow its prompt: touch the fingerprint sensor, look at the camera, enter your device PIN, or insert and touch your security key. - - After registering, your passkey appears in the list. You can rename it or delete it from the same section. + + In **Name this passkey**, type a name you will recognize later, such as "MacBook Touch ID" or "Work laptop", and select **Save**. - - Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore. - - - - - - On the login page, click Sign in with Passkey to authenticate without entering your password. + + The Name this passkey dialog with the name MacBook Touch ID typed in, and Cancel and Save buttons + + +The passkey appears under **Passkeys**. If you skip the name, it is listed as **Unnamed passkey**. You can add a passkey on each device you use. + + + The Sign-in and security section with a passkey named MacBook Touch ID listed under Passkeys, with a Remove button + + +## Sign in with a passkey + +On the sign-in page, select **Passkey** under **or continue with** and confirm on your device. Many browsers also suggest your passkey when you click the **Email Address** field. A passkey sign-in doesn't ask for a password or a two-step code. + +## Remove a passkey + +Select **Remove** next to the passkey. It is deleted straight away, without a confirmation, and can no longer sign you in. To finish the cleanup, also delete it from your device or password manager. + + + Passkeys can't be renamed after you save them. To change a name, remove the passkey and add it again. + + +## Related guides + +- [Signing in](/guides/signing-in): all the ways to get into your account. +- [Setting up two-factor authentication](/guides/setting-up-two-factor-authentication): a second check for password sign-ins. diff --git a/docs/guides/setting-up-two-factor-authentication.mdx b/docs/guides/setting-up-two-factor-authentication.mdx index fdea689db..cedfc4dea 100644 --- a/docs/guides/setting-up-two-factor-authentication.mdx +++ b/docs/guides/setting-up-two-factor-authentication.mdx @@ -1,78 +1,86 @@ --- title: "Setting up two-factor authentication" -description: "Enable TOTP two-factor authentication on your Reactive Resume account with an authenticator app, save recovery codes, and manage 2FA settings." +description: "Turn on two-step verification in Reactive Resume with an authenticator app, save your backup codes, and turn it off again when you need to." --- +Two-step verification adds a second check when you sign in: after your password, you enter a 6-digit code from an authenticator app on your phone or computer. Someone who learns your password still can't get in without it. + +## Before you start + +- Install an authenticator app, such as Google Authenticator, Microsoft Authenticator, 1Password, Bitwarden or Authy. +- Your account needs a password. If you signed up with a social account, the **Two-step verification** row doesn't appear until you [set a password](/guides/updating-your-profile#change-your-password). + +## Turn on two-step verification + - - The option to set up two-factor authentication only appears once your account has a password. If you signed in with Google or GitHub and never set one, you cannot follow this guide until you do. - - - Two-factor authentication requires a password to be set on your account. If you signed up using a social provider (Google or GitHub), you'll need to set a password first from the Authentication settings page. - - + + Select your name at the bottom of the sidebar, then **Settings**. On the **Account** page, find **Two-step verification** under **Sign-in & security**. - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. - - - If you haven't created an account yet, follow the guide on [Creating an account](/guides/creating-an-account). - + + The **Enable Two-Factor Authentication** dialog asks for your **Password**. Enter it and select **Continue**. + + The Enable Two-Factor Authentication dialog asking for your password, with a Continue button + - - Open Settings from your avatar and choose Account. Everything about signing in is under **Sign-in & security**. - - - - On the Authentication page, find the Two-Factor Authentication section and click the{" "} - Enable 2FA button. - - - - You are asked to re-enter your password to confirm it's really you. Enter it and click continue. - - - The password check prevents someone else from enabling two-factor authentication on your account. - + + In **Setup Authenticator App**, scan the QR code with your app. If you can't scan it, select the copy button next to the secret key above the code and paste the key into your app. + + The Setup Authenticator App dialog with a secret key and copy button, a QR code, six code boxes, and Cancel and Continue buttons + - - After entering your password, you are shown a QR code. Scan it with your authenticator device and enter the 6-digit code the app gives you to continue. - - - Popular authenticator apps you can use include: - - **Google Authenticator** - Available on iOS and Android - - **Microsoft Authenticator** - Available on iOS and Android - - **Authy** - Available on iOS, Android, and desktop - - **1Password** - Available on multiple platforms - - **LastPass Authenticator** - Available on iOS and Android - - - - If you prefer manual entry, copy the secret key above the QR code and paste it into your authenticator app. - - + + Type the code your app shows for Reactive Resume. The dialog moves on as soon as the code is complete, or you can select **Continue**. - Once verified, you are prompted to save backup codes. Each code works only once, and they let you sign in if you lose access to your authenticator device. - - - Make sure to copy and store these backup codes in a safe place. If you lose your authenticator device and don't have backup codes, you may lose access to your account. - - - - - - The next time you sign in, you are asked for a one-time code from your authenticator app. Enter the 6-digit code shown there to finish signing in. - - - If you have lost access to your authenticator device, use one of your backup codes to sign in. Each backup code can only be used once. - + **Copy Backup Codes** shows 10 one-time codes. Select **Download** to save them as a text file, or **Copy** to paste them somewhere safe, such as your password manager. Then select **Continue**. + + The Copy Backup Codes dialog listing ten codes in two columns, with Download, Copy and Continue buttons + + +The switch now shows two-step verification is on. From your next sign-in, Reactive Resume asks for a code after your password. + + + The Sign-in and security section with the Two-step verification switch turned on + + + + Two-step verification is on as soon as your code is accepted, even if you close the dialog before saving the backup codes. The codes are shown only once. If you lose your authenticator app and have no backup codes, you can't sign in with your password. + + +## Sign in with a code + +After your password, enter the code from your app on the **Two-Factor Authentication** page. If you don't have your app, select **Lost access to your authenticator?** and enter a backup code. Each backup code works once. See [Signing in](/guides/signing-in#enter-a-two-step-code) for details. + +Signing in with a [passkey](/guides/setting-up-passkeys) or a linked social account doesn't ask for a code. + +## Turn off two-step verification + + + + In **Sign-in & security**, turn off **Two-step verification**. + + + + In **Disable Two-Factor Authentication**, enter your **Password** and select **Disable 2FA**. + + + + + The Disable Two-Factor Authentication dialog with a Password field and a Disable 2FA button + + +To move to a new phone, turn two-step verification off and on again, then scan the new QR code on the new device. Your old codes and backup codes stop working. + +## Related guides + +- [Setting up passkeys](/guides/setting-up-passkeys): a faster sign-in that is also resistant to phishing. +- [Updating your profile](/guides/updating-your-profile#change-your-password): change or set your password. diff --git a/docs/guides/sharing-your-resume-publicly.mdx b/docs/guides/sharing-your-resume-publicly.mdx index 1435bb80e..b8efffd1f 100644 --- a/docs/guides/sharing-your-resume-publicly.mdx +++ b/docs/guides/sharing-your-resume-publicly.mdx @@ -1,242 +1,145 @@ --- -title: "Sharing your resume publicly" -description: "Publish your resume at a public URL, share it with recruiters, track views and downloads, and optionally protect it with a password." +title: "Sharing your resume with a link" +description: "Turn on a public link for your resume, choose its address, add a password, and see how many people viewed and downloaded it." --- -Reactive Resume can publish your resume at a **public URL**. Anyone with the link can open it, so you can send it to recruiters, collaborators, or people visiting your portfolio. Public resume URLs are not search-indexed by default. +A public link lets anyone you send it to open your resume in a browser, without an account. It always shows your latest changes, so you never have to send a new file after an edit. Links are off by default: until you turn one on, only you can see the resume. -## What public sharing gives you +## Before you start - - - Viewers always see the latest version of your resume. No need to send new files when you make updates. - - - See public view counters in the builder. - - - Optionally require a password so only people you trust can access your resume. - - - One URL you can paste into an email signature, LinkedIn, a portfolio, or a job application. - - +- Open the resume in the editor. Links belong to resumes; cover letters don't have one. +- Check your username in **Settings** → **Account**. It's part of every public address, for example `https://rxresu.me/dkowalski/game-developer`. If you host Reactive Resume yourself, your own address replaces `rxresu.me`. -## How to enable public sharing +## Turn on the public link - - Navigate to your resume in the resume builder. + + In the editor bar, select **Share** (or press ⌘ ⇧ S, Ctrl Shift S on Windows and Linux). The **Share & export** sheet opens on its **Link** tab. + + + Editor bar with the History and Assistant icons, a Share button with a green Public badge, and the Download PDF button + + + Turn on **Public link**. The link is live at once, and **Share** in the editor bar gains a **Public** badge so you can tell at a glance. -In the **right sidebar**, select **Sharing**. - - - Turn on the **Allow Public Access** switch. Your resume is then reachable at its public URL. - - - Sharing section showing the public access control - - - - - Your public URL is displayed below the toggle. It follows this format: - - ``` - https://rxresu.me/{username}/{slug} - ``` - - Click the **copy** button to copy the URL to your clipboard. + + Link tab of the Share and export sheet with the Public link switch off, a greyed-out Address field and a message about counting views + + + + Select **Copy** next to the **Address** field. The button shows **Copied** for a moment. Paste the link into an email, a job application or your LinkedIn profile. + On a phone or tablet, you can also select **Share via…** to send the link through your device's share menu. - - The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your - slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details. - + + Link tab with Public link on, the address and a Copy button, switches for visitor downloads and a password, Open public page and QR code buttons, and view and download counts + -## How the public URL works +With the link on, the tab also offers: -When someone visits your public resume URL: +- **Visitors can download the PDF**: on by default. Turn it off if you want people to read the resume on the page but not save a copy. It also stops visitors from printing the page. +- **Open public page**: opens the resume in a new tab, the way visitors see it. +- **QR code**: shows a code that opens the link, handy for a printed card or a slide. -1. They see the live version. The page renders your current resume data, including your latest changes. -2. No account is required. Visitors don't need a Reactive Resume account to view or download your resume. -3. They can download a PDF. The public page shows an identity header with your name, headline, and picture, plus a **Download PDF** action. -4. Link previews work. Reactive Resume generates per-resume Open Graph and Twitter card previews, so links you paste into LinkedIn, Slack, iMessage, and similar apps show your name and headline instead of a generic placeholder. -5. Views are tracked. Visits are counted in your resume statistics (see below). +Public links aren't listed anywhere and ask search engines not to index them. People find your resume only through a link you share. - - Changes you make in the builder show up immediately on the public URL. There is no separate "publish" step. Treat the - link as something you send to people directly, not as a search profile page. - +## Change the address -## Resume language +The last part of the address is yours to choose. It starts out based on the resume's name. -Set **Page → Language** in the builder's right sidebar to choose the language of default section headings. Public visitors and PDF downloads use this saved resume language, even when the visitor's app interface uses another language. Section titles you rename remain as written. + + + With the link on, type a new ending in the **Address** field. Use lowercase letters, numbers and single dashes; spaces turn into dashes as you type. + + + Reactive Resume checks the address as you type. When it shows **Available**, the new address is saved on its own. Until then, the old address keeps working. + + -The interface language selected in Preferences is stored in the current browser. It does not change the resume's language or sync to other browsers. + + Address field outlined in red containing game_developer, with the message: Use lowercase letters, numbers and single dashes + -## Tracking public engagement +If another of your resumes already uses that address, the message names it and suggests a free one. Select **Try** followed by the suggestion to use it. -When your resume is public, Reactive Resume counts public views, so you can tell whether people are opening the link you shared. +After a change, the old address still leads to your resume for 30 days, so links you already sent keep working while you update them. Changing your username is different: every public address changes at once, and old links stop working. -### Where to find statistics +## Require a password -In the resume builder, open the **right sidebar** and select **Statistics**. +A password keeps the link private to the people you give it to, for example while you're job hunting and don't want a current employer to stumble on your resume. -You'll see public view information such as: + + + On the **Link** tab, turn on **Require a password**. + + + Type a password of 6 to 64 characters, type it again in **Confirm Password**, then select **Set Password**. -| Metric | Description | -| ------------------- | ---------------------------------------------------------------- | -| **Views** | Number of times your public resume page was visited | -| **Downloads** | Number of times a visitor downloaded your resume as a PDF | -| **Last viewed** | The date when your resume was last viewed | + + Dialog titled Protect your resume with a password, with Password and Confirm Password fields and Cancel and Set Password buttons + + + + Share the password through a different channel than the link, such as a text message. Anyone with both can view and download the resume. + + -Each metric also shows a 30-day sparkline and the change against the previous period, so you can see whether interest rose or fell after you shared the link. +To remove the password, turn off **Require a password** and select **Remove** to confirm. Anyone with the link can then view the resume. - - Statistics are only shown after public sharing is enabled. If you turn off public access, existing stats are - preserved. - +## What visitors see -### What counts as a view? +Visitors open your resume on a clean page with no Reactive Resume menus. -A view is counted each time someone loads your public resume page. This includes: +- **On a computer**, your name, headline and location sit at the top with **Copy link** and **Download PDF**. Below is the resume, exactly as it prints. +- **On a phone**, the resume reflows into readable text in the same order as the pages, with **Download PDF** and a share button pinned to the bottom of the screen. +- **With a password**, visitors first see **This resume is password protected** and enter the password you shared, then select **Unlock**. The page stays unlocked for about 10 minutes; after that, they enter it again. You see this screen too when you open your own link. +- **When the link is off**, or the resume is in Trash, visitors see **This resume isn't shared right now.** The page gives nothing away about you. -- Direct visits to your public URL -- Clicks from links you've shared + + Public resume page with David Kowalski's name and headline in a top bar, Copy link and Download PDF buttons, and the resume page below + -Owner self-visits while you are signed in to your account are not included in the view statistics. + + Public resume on a phone screen, reflowed into text with contact buttons, Education and Experience sections, and a pinned Download PDF button + + + + Page titled This resume is password protected, with a Password field and an Unlock button + + +Visitors never see your private notes, or any section or entry you've hidden. At the bottom of the page, a small **Made with Reactive Resume, free and open source** line links to the Reactive Resume home page. + +## See views and downloads + +The bottom of the **Link** tab shows **Views and downloads**. Only you see these numbers. + +| Number | What it counts | +| --- | --- | +| **views · 30 days** | Visits to your public page in the last 30 days | +| **downloads** | PDFs visitors downloaded from the page in the last 30 days | +| **since last view** | How long ago someone last opened the page, such as `5m`, `2h` or `3d` | + +A bar chart under the numbers shows views for each of the last 30 days. + +- Your own visits and downloads don't count while you're signed in. +- Repeat visits from the same visitor within an hour count once. +- Counts are anonymous. Reactive Resume doesn't record who viewed your resume. +- Counting starts only once the link is on. + +## Turn off the public link + +Turn off **Public link** on the **Link** tab. The link stops working immediately, and visitors see **This resume isn't shared right now.** Your address is kept, so turning the link back on brings the same address back. Moving the resume to Trash also stops its link. - Only **you** can see your resume's view statistics. Visitors to your public URL cannot see how many views your resume - has. + While a resume is locked, you can't change its link settings. Select the resume's name in the editor bar, then **Unlock editing** first. Share and Download are also unavailable while you're offline. -## Password protecting your resume +## Related guides -To share your resume with specific people while keeping it away from everyone else, add password protection. - -When password protection is enabled: - -- Visitors must enter the correct password to view your resume -- The password prompt appears before any resume content is shown -- You can share the password separately with trusted individuals - -### How to set a password - - - - First, make sure **Allow Public Access** is turned on in the **Sharing** section. - - -Below the public URL, click **Set Password**. - - - Type a password (6-64 characters) and confirm it. Viewers have to enter this password to see your resume. - - - - Share the password with your intended audience through a secure channel (e.g., direct message, email). - - - - - Choose a password you're comfortable sharing. Anyone with the password can view and download your resume. - - -### How to remove password protection - -If you no longer need password protection: - -1. Go to the **Sharing** section in the right sidebar -2. Click **Remove Password** -3. Confirm the action - -Your resume is then open to anyone with the public URL. - -## Use cases for public sharing - - - - Add your public resume URL to your LinkedIn profile's **Featured** section or **Contact Info**. Recruiters can view your detailed resume directly. - - - - Include your resume link in your email signature, so anyone you write to can open it. - - - - Embed or link to your resume from your personal website. The link always shows your latest resume. - - - - Some applications accept a link to your resume, while others require a file upload. Use the public URL when a link is - accepted, and export a PDF when an upload is required. - - - - Share your resume URL via QR code or NFC. Update the resume before the event and everyone gets the current version. - - - - Use password protection to share your resume only with specific recruiters while keeping it hidden from your current employer. - - - -## Turning off public access - -To make your resume private again: - -1. Go to the **Sharing** section in the right sidebar -2. Turn off the **Allow Public Access** switch - -When public access is disabled: - -- Your public URL returns a "not found" error -- Existing links stop working immediately -- Your statistics are preserved (they'll resume if you re-enable public access) -- Password protection settings are preserved - - - To hide your resume temporarily, use password protection instead of turning off public access. The URL stays active - for people who have the password. - - -## Copying the URL from the builder dock - -The builder dock has a **Copy URL** shortcut. It copies the same public URL shown in the **Sharing** section. - - - Copying the URL does not enable public access. Turn on **Allow Public Access** in the **Sharing** section before sending - the link to someone else. - - -## Frequently asked questions - - - - Yes. The URL is based on your **username** and the resume's **slug**. You can change the slug in the **Update Resume** dialog. To open it, right-click your resume card in the dashboard and select "Update". The username is set in your account settings. - - - - No. By default, public resume URLs are meant for human recipients who receive the link from you, not as search profile - pages. If you want tighter access control, use password protection or keep your resume private. - - - - No. If you are signed in as the owner, your own visits are excluded from the view statistics. - - - - No, Reactive Resume tracks public view counts, not the identity of visitors. This protects visitor privacy. - - - - Your public URL changes to match the new username, and the old URLs stop working immediately. Update any links you have already shared. - - +- [Exporting your resume](/guides/exporting-your-resume): download a PDF, Word, Markdown or JSON file instead of sending a link. +- [Updating your profile](/guides/updating-your-profile): change the username that appears in your public addresses. +- [Checking your resume](/guides/checking-your-resume): review your resume before you share it. +- [Using the Trash](/guides/using-the-trash): what happens to a shared resume you delete. diff --git a/docs/guides/signing-in.mdx b/docs/guides/signing-in.mdx new file mode 100644 index 000000000..1d74dc9b8 --- /dev/null +++ b/docs/guides/signing-in.mdx @@ -0,0 +1,92 @@ +--- +title: "Signing in" +description: "Sign in to Reactive Resume with your email or username, a passkey, or a social account, reset a forgotten password, and enter two-step codes." +--- + +This guide covers every way to get into your account, including what to do when you have forgotten your password or lost your authenticator app. + +## Sign in with your password + + + + Go to [rxresu.me](https://rxresu.me) and select **Build your resume**. If you are not signed in, Reactive Resume takes you to **Sign in to your account**. + + + + The **Email Address** field also accepts your username. + + + + Select the eye icon next to the field to check what you typed. + + + +If you have turned on two-step verification, Reactive Resume asks for a code next. See [Enter a two-step code](#enter-a-two-step-code) below. + + + The Sign in to your account page with Email Address and Password fields, a Forgot Password? link, a Sign in button, and Passkey, Google, GitHub and LinkedIn buttons + + +## Sign in with a passkey + +If you have [added a passkey](/guides/setting-up-passkeys), select **Passkey** under **or continue with** and confirm with your fingerprint, face, device PIN or security key. Many browsers also offer your passkey as a suggestion when you click the **Email Address** field. + +Signing in with a passkey skips the password and the two-step code. + +## Sign in with Google, GitHub or LinkedIn + +Select the provider's button and approve the request on its site. The buttons appear only when the site has set them up. Self-hosted copies may show a single button named after your organization's sign-in service instead. + +A social button signs you in to the account that is [linked to that provider](/guides/linking-social-accounts). If no account uses that email yet, a new one is created, unless the site has turned off new sign-ups. + +## Reset a forgotten password + + + + The link sits next to the **Password** label on the sign-in page. + + + + Type the email address on your account and select **Send Password Reset Email**. + + + + On the **Reset your password** page, enter a **New Password** of at least 8 characters and select **Reset Password**. You return to the sign-in page, where you can use the new password. + + + + + The Forgot your password? page with an Email Address field and a Send Password Reset Email button + + + + Password reset needs email delivery. On a self-hosted copy without email set up, ask the person who runs it for help. + + +## Enter a two-step code + +After your password, the **Two-Factor Authentication** page asks for the 6-digit code from your authenticator app. Type it in. The page submits as soon as the sixth digit is in, or you can select **Verify**. + + + The Two-Factor Authentication page with six code boxes, Back to sign in and Verify buttons, and a Lost access to your authenticator? link + + +### Use a backup code instead + +If you don't have your authenticator app, select **Lost access to your authenticator?**, enter one of the backup codes you saved when you turned on two-step verification, and select **Verify**. + +Each backup code works once. Type it without the hyphen: the field holds 10 characters, and the codes are shown as two groups of five (`82cNK-qaOiN` becomes `82cNKqaOiN`). + + + The Verify with a Backup Code page with a single text field, a Go Back button and a Verify button + + +## Sign out + +Select your name at the bottom of the sidebar, then **Sign out**. You can also find **Sign out** at the bottom of **Settings → Account**. + +## Related guides + +- [Creating an account](/guides/creating-an-account): sign up if you don't have an account yet. +- [Setting up two-factor authentication](/guides/setting-up-two-factor-authentication): turn codes on or off and get backup codes. +- [Setting up passkeys](/guides/setting-up-passkeys): sign in without a password. diff --git a/docs/guides/tailoring-a-resume-for-a-job.mdx b/docs/guides/tailoring-a-resume-for-a-job.mdx new file mode 100644 index 000000000..6783ae3c6 --- /dev/null +++ b/docs/guides/tailoring-a-resume-for-a-job.mdx @@ -0,0 +1,84 @@ +--- +title: "Tailoring a resume for a job" +description: "Copy your resume for one job, write a matching cover letter, and use Prepare for next step to get help with fit, follow-ups and interviews." +--- + +A resume written for one job usually does better than a general one. From any application you can make a copy of your resume that's linked to that job, write a cover letter for it, and later ask the assistant to help you prepare for the next step. Because the copy is linked to the application, [Check](/guides/checking-your-resume) and [the assistant](/guides/using-the-assistant) can compare it with the saved job posting. + +## Before you start + +- Add the job to **Applications**, ideally with its posting. See [Adding an application](/guides/adding-an-application). +- Have at least one resume to start from. See [Creating your first resume](/guides/creating-your-first-resume). +- The assistant's suggestions need an AI provider. See [Connecting an AI provider](/guides/using-ai). You can still make the copy without one. + +## Make a copy of your resume for the job + + + + In **Applications**, select the application to open its details. + + + It's under **What you sent**, and only shows when no resume is linked yet. (When adding a new application, **Add and tailor a resume** takes you to the same place.) + + + Under **Start from**, pick the resume to copy. Usually that's your main, general resume. + + + Under **For which job?**, the application is already picked. **Name** is suggested from the resume and the company, such as "Game Developer Resume — Maple & Moss". Change it if you like. + + + Select **Create and open**. + + + + + Copy a resume for a job dialog with Game Developer Resume selected under Start from, Maple & Moss selected under For which job?, and the suggested name Game Developer Resume — Maple & Moss, with a Create and open button + + +Reactive Resume makes the copy, links it to the application (it becomes the application's resume if none was linked), and opens it in the editor with the assistant ready. Your original resume doesn't change. + +To tailor the copy: + +- In the assistant, pick **Tailor to the *company* posting**. The assistant reads the resume and the posting, and suggests edits you accept or reject one at a time. See [Using the assistant](/guides/using-the-assistant). +- Switch to **Check** mode to see how well the resume matches the posting. See [Checking your resume](/guides/checking-your-resume). + + + You can also start a copy from **Documents**: open a resume's **⋯** menu and select **Copy for a job…**, or select **New** and then **Copy a resume for a job**. Pick the application under **For which job?**, or **No job yet** for a plain copy. + + + + What you sent section with a Tailor a resume button, a Write a letter button and an Attach a file instead link + + +## Write a cover letter for the job + +In the application's details, under **What you sent**, select **Write a letter**. Reactive Resume creates a cover letter named "Cover letter — *company*", fills in the recipient from the application, links it to the application and to the application's resume (if it has one), and opens it in the letter editor. + +To link a letter you already have, go to **Documents**, open the letter's **⋯** menu and select **Link to application…**. See [Writing a cover letter](/guides/writing-a-cover-letter). + +Once the application reaches **Applied**, the version of the resume and letter you sent is saved, so you can open exactly what went out later. See [Managing an application](/guides/managing-an-application#seeing-what-you-sent). + +## Prepare for the next step + +When an application has a linked resume or letter, **Prepare for next step** at the bottom of its details opens that document with the assistant and a set of suggestions for this job: + +- **How well do I fit this role?** compares your resume with the posting. +- **Draft a follow-up email** writes a short, polite note for the recruiter. +- **Prepare me for the interview** lists likely questions, based on the posting and your resume. +- **Tailor to the *company* posting** suggests edits to fit the posting. + + + Assistant panel reading What should we work on? I can see this resume and the Northwind Games posting, with suggestions How well do I fit this role?, Draft a follow-up email, Prepare me for the interview and Tailor to the Northwind Games posting + + +Pick a suggestion, or type your own request. If the application has both a resume and a letter, the resume opens. The button is disabled until a resume or letter is linked. + + + The assistant uses the posting of the application a resume was made for. A general resume linked to several applications uses the one you updated most recently, which may not be the one you started from. For the most relevant help, prepare from a copy made for this job. + + +## Related guides + +- [Using the assistant](/guides/using-the-assistant): how suggestions, accepting and rejecting work. +- [Checking your resume](/guides/checking-your-resume): see how your resume matches a job. +- [Viewing application insights](/guides/viewing-application-insights): compare replies for tailored and general resumes. diff --git a/docs/guides/tracking-job-applications.mdx b/docs/guides/tracking-job-applications.mdx index cf7358319..282847a05 100644 --- a/docs/guides/tracking-job-applications.mdx +++ b/docs/guides/tracking-job-applications.mdx @@ -1,161 +1,113 @@ --- title: "Tracking job applications" -description: "Use the Application Tracker to record jobs, link the resume you sent, manage follow-ups, and move applications through your hiring pipeline." +description: "Keep every job you apply for in one place: see what needs you next, move applications through stages, and switch between List, Board, Insights and Calendar." --- -The **Application Tracker** keeps your job search tied to the resumes you build in Reactive Resume. Each application can store the company, role, stage, job posting, resume, cover letter, contacts, notes, and follow-up details. +**Applications** is where you keep track of the jobs you're going for. Each application holds the role, the company, the saved job posting, the resume and letter you sent, your contacts, notes and a dated history. It also works out what each application needs next, such as an interview coming up or a follow-up that's due. -## Open the Application Tracker +To open it, select **Applications** in the sidebar (on a phone, it's in the bottom tab bar). - - - Open Reactive Resume and sign in to your account. - - - - In the dashboard sidebar, click **Applications**. - - - - - Application Tracker showing search, tag filters, board columns, and application cards grouped by stage + + Applications page showing a follow-up reminder banner, the List, Board, Insights and Calendar tabs, a search field, Show closed, and applications grouped under Interview, Offer, Screening, Applied and Saved - - Press `Cmd/Ctrl+K` from anywhere in the app to open the command palette. Select **Applications** to search your pipeline by company or role, or run **New Application** to jump straight to the add form. - +## How stages work -## Add an application - - - - If this is your first application, use the button in the empty state. Otherwise, use **Add application** in the page header. - - - - Add the **Company** and **Role / title**. These two fields are required. You can also add the location, salary range, - source, tags, notes, and follow-up details. - - - - Pick the current stage of the opportunity. - - - - Pick a Reactive Resume in the **Resume** field to keep a live link to it. You can also upload the exact resume PDF you - sent. - - - - If you sent a cover letter, attach the PDF in the **Cover letter** field. - - - - Click **Add to pipeline**. - - - - - Add application form with job posting auto-fill, company, role, stage, resume, cover letter, tags, and follow-up fields - - - - Linking a Reactive Resume enables AI match scoring and resume tailoring for that application. - - -## Use AI to fill a job posting - -If you have an AI provider configured, paste a job posting URL at the top of the add form and click **Auto-fill**. Reactive Resume reads the posting and fills in what it can, such as company, role, location, salary, and job description. - -If auto-fill fails or the posting is private, paste the job description manually. - -For AI setup, see [Using artificial intelligence](/guides/using-ai). - -## Understand application stages - -Applications move through a fixed set of stages: +Every application sits in one stage. The first five stages form a pipeline you move through in order: | Stage | Use it when | | --- | --- | -| **Saved** | You found a role but have not applied yet. | -| **Applied** | You submitted the application. | -| **Screening** | A recruiter, hiring manager, or automated process is reviewing you. | -| **Interview** | You are in an interview process. | +| **Saved** | You found the job but haven't applied yet. | +| **Applied** | You sent your application. | +| **Screening** | A recruiter or hiring manager got back to you, or you have a first call. | +| **Interview** | You're in the interview process. | | **Offer** | You received an offer. | -| **Rejected** | The company declined or the opportunity ended. | +| **Closed** | The application ended, whatever the outcome. | -You can move applications by dragging cards on the board, using the action menu, using bulk actions in the table, or opening an application and clicking **Move to**. +You can close an application from any stage. Closing asks for a reason (**Not selected**, **I withdrew**, **Accepted another offer** or **No response**), which Insights uses later. Closed applications are hidden until you turn on **Show closed**. See [Managing an application](/guides/managing-an-application#closing-an-application). -## Choose a view + + When an application with a linked resume or letter reaches **Applied** or a later stage, Reactive Resume saves a copy of each as a "Sent to *company*" version. You can always open exactly what you sent, even after you keep editing the document. + -The Application Tracker has three views: +## Choosing a view -| View | Best for | -| --- | --- | -| **Board** | Moving applications through stages visually. | -| **Table** | Reviewing many applications, selecting rows, and making bulk updates. | -| **Insights** | Understanding your pipeline, response rate, interviews, offers, sources, and application velocity. | +Use the tabs above the list to switch views. Your choice is kept in the page address, so a bookmark opens the same view. -Use search, tag filters, sorting, and the archived toggle to narrow the list. + + + The default view. Applications are grouped by stage in the order that usually needs you first: **Interview**, **Offer**, **Screening**, **Applied**, **Saved**, then **Closed** when shown. - - Application Tracker insights view showing pipeline metrics, a funnel chart, applications over time, and source counts + - Select a group heading to collapse or expand it. + - Select the **Role**, **Next step** or **Updated** column heading to sort within each group. Select it again to reverse the order. + - The **Next step** column shows the next interview, the follow-up you set, or how long you've been waiting. It turns amber when something is overdue or you've had no reply for 10 days or more. + - The **Sent** column shows an icon for a linked resume and a linked letter. It says **None** in amber when an application was sent without a linked document. + - Select a row to open its details. + + + One column per stage, with a card for each application. Drag a card to another column to move it to that stage. Each card also has a **⋯** menu with **Move to…**, **Close…** and **Delete…**, which is handy if you prefer the keyboard. When **Show closed** is on, a **Closed** column appears too. Dragging a card there closes it without a reason, so use **Close…** if you want Insights to know why. + + + Board view with Saved, Applied, Screening and Interview columns, each card showing the role, company and next step + + + + Card menu with Move to…, Close… and Delete…, and the Move to submenu listing Saved, Applied, Screening, Interview and Offer + + + + Charts about how your applications are going: how far they get, how often and how quickly people reply, and where your applications come from. See [Viewing application insights](/guides/viewing-application-insights). + + + A month calendar of your interviews, with a list of upcoming ones beside it. See [Scheduling interviews](/guides/scheduling-interviews). + + + +On phones, the page shows **List** and **Insights** only, and list rows show the company and next step on a second line. + +## Finding applications + +Type in **Search role, company or contact** to filter the list as you type. Search looks at the role, company, location, contact names and tags. If nothing matches, select **Clear search**. + +**Show closed** adds closed applications to the List, Board and Calendar views. Insights always counts every application. + +## Acting on several applications at once + +In the List view on a computer or tablet, select the checkboxes next to the applications you want. A bar appears at the bottom of the page with: + +- **Move to…** to move them all to one stage +- **Add tag** to add the same tag to each +- **Close…** to close them all with one reason +- **Delete…** to delete them permanently (you're asked to confirm) +- **Clear** to deselect + + + Two application rows checked in the List view and a dark bar reading 2 selected with Move to…, Add tag, Close…, Delete… and Clear -## Update an application +## Follow-up reminders -Open an application from the board or table to view its detail panel. From there you can: +When an application has been at **Applied** for 10 days or more without a reply, a reminder appears above the list for the one that has waited longest, for example "Harborlight Labs: no reply for 17 days." Select **Open** to go to that application, or **×** to dismiss the reminder. Dismissing is remembered in this browser, so that application's reminder doesn't come back here. -- edit the application details; -- move it to the next stage; -- open the original job posting; -- attach or replace resume and cover letter PDFs; -- add contacts; -- add follow-up information; -- add notes to the timeline; -- archive, unarchive, mark rejected, or delete the application. +## Opening Applications from anywhere -Stage changes and notes appear in the timeline automatically. +Press ⌘ K (Ctrl K on Windows and Linux) and choose **Applications** to go to the page or to search your applications by company or role. Run **New Application** to open the add dialog. See [Using the command bar](/guides/using-the-command-bar). - - Application detail panel showing stage progress, linked resume, Application Copilot, contacts, and timeline activity - +## Related guides -## Use Application Copilot + + + Paste a job link or posting and start tracking it. + + + Stages, next steps, what you sent, notes, contacts and history. + + + Make a copy of your resume for one application, or write a letter. + + + Bring in a spreadsheet, or export your applications. + + -Application Copilot appears in the application detail panel. It can: - -- score how well the linked resume matches the job description; -- create a tailored copy of the linked resume; -- draft a cover letter; -- draft a follow-up message. - -Match scoring and resume tailoring require both a linked Reactive Resume and a job description. Drafting also works from the application and resume context available in the tracker. - - - Review AI-generated content before sending it. You are responsible for the final resume, cover letter, and follow-up message. - - -## Manage applications from an MCP client - -You can also manage the Application Tracker from an MCP-compatible AI client. This lets an agent list applications, create new records, move stages, add notes, attach the PDFs you sent, and run Application Copilot without opening the web app. - -For setup and prompt examples, see [Managing applications with MCP](/guides/managing-applications-with-mcp). - -## Archive or delete applications - -Archive an application to hide it from the active board without losing its history. Use the **Archived** toggle to see archived applications and unarchive them later. - -Delete an application only when you no longer need its record. Deleting an application removes its timeline and cannot be undone. +You can also manage applications from an AI client over MCP. See [Managing applications with MCP](/guides/managing-applications-with-mcp). diff --git a/docs/guides/undoing-changes-and-version-history.mdx b/docs/guides/undoing-changes-and-version-history.mdx index 307e0a60e..f78764c65 100644 --- a/docs/guides/undoing-changes-and-version-history.mdx +++ b/docs/guides/undoing-changes-and-version-history.mdx @@ -1,106 +1,127 @@ --- title: "Undoing changes and version history" -description: "Undo and redo edits with keyboard shortcuts in the Reactive Resume builder, and restore an earlier snapshot from the version history menu." +description: "Undo recent edits in the Reactive Resume editor, save named versions, and preview or restore an earlier version of a resume or cover letter." --- -Reactive Resume keeps two layers of change history for every resume: - -- **Undo and redo**: a live timeline of the changes you've made in the current builder session. -- **Version history**: server-side snapshots taken at meaningful moments, kept even after you close the builder. - -Use undo for a quick correction. Use version history to jump back to an earlier editing snapshot, import, or AI/API edit. +Reactive Resume gives you two ways back. **Undo** reverses your last few edits while the resume is open. **History** +keeps saved versions of the document on the server, so you can look at yesterday's resume, or the one you sent to a +company, and bring it back. ## Undo and redo -Every change in the builder is undoable: typing, drag-and-drop reordering, template and layout switches, and edits applied by the AI assistant. +In the resume editor, undo steps back through your changes: typing, adding or deleting entries, moving sections, +template and design changes, custom styles, and edits you accept from the Assistant. - - - Undo history is scoped to the resume you have open. - +| Action | Mac | Windows and Linux | +| --- | --- | --- | +| Undo | ⌘ Z | Ctrl Z | +| Redo | ⌘ Shift Z | Ctrl Shift Z or Ctrl Y | - - Use either the toolbar buttons on the floating dock or a keyboard shortcut: +These shortcuts undo resume-wide changes when your cursor isn't in a text field. Inside a field, they undo your typing +in that field. Click outside the field first to undo a change such as a template switch. - | Shortcut | Action | - | --- | --- | - | `Cmd/Ctrl+Z` | Undo the last change | - | `Cmd/Ctrl+Shift+Z` | Redo the last undone change | +Many actions also show a message with an **Undo** button right after you make them, for example deleting an entry, +switching template, **Fit to one page** or **Reset to template defaults**. - Rapid typing collapses into a single step, so one undo removes a phrase rather than one letter. - - +How undo behaves: + +- **Typing groups into one step.** Edits to the same field less than a second apart undo together, so one undo + removes a phrase rather than a letter. +- **Up to 200 steps** are kept. +- **Undo lasts while the resume is open.** Reloading the page or opening the resume again starts a fresh undo history. + So does restoring a version, or a change arriving from somewhere else, such as another tab or an API client. + +The cover letter editor has no document-wide undo. Use ⌘ Z inside the text you're editing, or +restore a version from History. - When your cursor is inside a text field, `Cmd/Ctrl+Z` falls back to your browser's native input undo, so you can undo - just the characters you typed. Click outside the field, or use the dock buttons, to undo builder-wide changes such as - a template switch. + Changes that couldn't be saved, for example because you went offline or closed the tab, are kept on your device. The + next time you open the resume, they come back and are saved, and the editor tells you it restored them. -Undo history lives in your browser for the current session. Reloading the builder clears it, so use version history for anything older. +## Open History -## Version history +On a desktop-width window, select the **History** icon (a clock) in the editor bar, next to the Assistant icon. +History opens in the **Share & export** panel on its **History** tab. On a narrower window, select **Share**, then the +**History** tab. -Reactive Resume snapshots your resume automatically: + + The History tab of the Share & export panel: a field to name the current version, then a timeline with Now, a named version marked with a bookmark, an Editing session and Created + -- when you import a resume; -- when the AI assistant or API applies edits; -- on periodic saves during editing, including template switches; -- when you restore a version. +Each version shows a title, when it was saved (for example "Today 14:02", "Yesterday 09:15" or a date), and how it was +saved. History shows the 100 most recent versions. -Snapshots are stored on the server, per resume, and are kept across sessions. +## What gets saved automatically -Reactive Resume keeps a rolling window of the 30 most recent snapshots for each resume. Periodic editing snapshots, including template changes, are throttled to at most one every two minutes. Imports, AI/API edits, and restores create their own checkpoints. +You don't need to save anything for History to work. Versions are added when: -### Open version history +| Title in History | Saved when | Kept | +| --- | --- | --- | +| **Created** / **Imported** | You create or import the resume. | Until you delete the resume | +| **Editing session** | You edit. Each visit to the editor keeps one version with its latest state, refreshed at most every two minutes. | 90 days | +| **AI edit** | A change arrives through the API, for example from an MCP client or another AI tool connected to your account. Edits you accept from the Assistant in the editor count as part of your editing session. | 90 days | +| **Sent to** *company* | An application linked to this resume first moves to **Applied**, **Screening**, **Interview** or **Offer**. | Until you delete the resume | +| **Before restore** | Just before you restore a version. | Until you delete the resume | +| **Restored a version** | Right after you restore a version. | 90 days | +| *Your name* | You save a named version. | Until you delete it | -Click the **clock** icon in the builder header, next to the resume name, to open the version history menu. +Resumes and cover letters both have History. A letter's versions work the same way; only the kinds that apply to +letters appear. -The menu lists recent snapshots newest first, each with a label describing what triggered it and a relative timestamp such as *2 hours ago*. +## Save a named version -### Restore a version +Name a version before a big change, or when you send the resume somewhere, so it's easy to find later. - - Click the clock icon in the builder header. + + Select the **History** icon, or **Share** then **History**. - - - Select the entry you want to restore. Reactive Resume asks you to confirm before replacing the current data. + + In **Name this version**, type a short name such as "Sent to Lumen" or "Before tailoring" (up to 80 characters). - - - The resume is updated to the snapshot's contents and reloads in the preview. + + Select **Save**. The version appears in the timeline with a bookmark icon and the detail "named". -Restoring is **non-destructive**: it writes the older snapshot back through the normal update path, so: +To rename or delete a named version, point to it, select **⋯**, then choose **Rename…** or **Delete**. Only named +versions can be renamed or deleted; the others are kept for as long as the table above says. -- your previous versions are still listed in the menu; -- the restore itself becomes a new snapshot; -- if you change your mind, you can restore the pre-restore version, or press `Cmd/Ctrl+Z` to undo the restore. +## Preview and restore a version - - Only the resume owner can list or restore versions. A locked resume cannot be edited or restored until you unlock it - from the dashboard. - + + + Select any version in the timeline. The page shows that version, read-only, and a bar at the top of History says + which version you're viewing. + + + Select **Restore this version** to make it your current resume, or **Back to now** to leave it. Closing the panel + also takes you back to now. + + -## Keep longer owner-managed history with Git + + The History tab with a dark bar reading Viewing Today 01:44 AM · Original for game studios · read-only, with Restore this version and Back to now buttons, and the named version selected in the timeline + -In-app version history and Git backups solve different problems: +Restoring never throws anything away. Before it replaces your resume, Reactive Resume saves the current state as +**Before restore**, then adds **Restored a version** after it. To undo a restore, restore **Before restore**. -- **In-app version history** is automatic, stored by Reactive Resume, and limited to the 30 most recent rolling snapshots for one resume. -- **Git history** contains only the JSON exports you choose to commit. It is stored in your own local repository, uses your commit messages, and follows the retention you choose. + + The History timeline after a restore: Now, Restored a version, Before restore, the named version, Editing session and Created + -Git backup is manual. Reactive Resume does not create commits, synchronize with a repository, or upload files to a remote. To set up a local repository and recover a committed export as a new resume, see [Keep JSON backups in a local Git repository](/guides/exporting-your-resume#keep-json-backups-in-a-local-git-repository). + + Restoring clears the editor's undo history, so ⌘ Z can't reverse it. Use **Before restore** + instead. A locked resume can't be restored over; select **Unlock editing** in the document menu first. + -## Which to use when +A version holds the whole resume: content, design, custom styles and private notes. Its public link settings aren't +part of it. -| Situation | Use | -| --- | --- | -| You typed a wrong word or moved an item you didn't mean to. | Undo (`Cmd/Ctrl+Z`) | -| You just switched templates and want the old one back. | Undo; version history can help only after a periodic editing snapshot captures the switch. | -| You imported a resume and want to compare with what you had before. | Version history | -| The AI assistant applied edits you no longer want. | Undo the batch, or restore the pre-AI snapshot. | -| You closed the browser and want to roll back yesterday's changes. | Version history | -| You want selected backups beyond the 30-snapshot rolling window. | Export JSON and commit it to your own Git repository. | +## Related guides + +- [Tracking job applications](/guides/tracking-job-applications): applications keep the exact version you sent. +- [Using the Assistant](/guides/using-the-assistant): accepted edits can be undone like any other change. +- [Keyboard shortcuts](/guides/keyboard-shortcuts): every editor shortcut in one place. diff --git a/docs/guides/updating-your-profile.mdx b/docs/guides/updating-your-profile.mdx index ff1c90840..8d396b718 100644 --- a/docs/guides/updating-your-profile.mdx +++ b/docs/guides/updating-your-profile.mdx @@ -1,41 +1,72 @@ --- title: "Updating your profile" -description: "Update your Reactive Resume profile information, including your display name, username, email address, and profile picture from account settings." +description: "Change your account photo, name, username, email address and password in Reactive Resume's Settings, and learn what each change affects." --- +Your profile is the name, photo, username and email tied to your account. You can change any of them in **Settings → Account**. This page also covers changing your password. + + + Your profile is separate from your resumes. Changing your account name or photo does not change the name or picture on any resume. Those live in each resume's **Basics** section. + + +## Open your profile + +Select your name at the bottom of the sidebar, then **Settings**. **Account** opens first, with **Profile** at the top. + +On a phone, **Settings** opens a short list first. Tap **Account**. + + + The Profile section with an initials avatar, a Change photo button, Name and Username fields, and an Email field with a hint about confirmation + + +## Change your name, username or email + +Click a field, type the new value, then press Enter or click anywhere else. The change saves as soon as you leave the field. Press Esc before leaving to undo what you typed. + +If a value can't be saved, it stays in the field with the reason underneath, for example when a username is already taken. + +- **Name**: up to 64 characters. Shown in the app, not on your resumes. +- **Username**: 3 to 64 characters, using lowercase letters, numbers, dots, hyphens and underscores. The field shows your site address in front of it, because the username is part of every public resume link. +- **Email**: see below. Changing it takes an extra step. + + + Changing your username changes the address of every public resume. Links you have already shared, such as the one on a job application, stop working. Share the new links after the change. + + +### Confirm a new email address + +When you change **Email**, Reactive Resume sends a confirmation link to the new address and shows a message saying so. Your account keeps the old address until you open that link. Check the new inbox, including spam, and follow the link to finish. + +If your current address isn't verified yet, the hint under the field offers **Not verified yet. Resend the link**. On self-hosted copies without email delivery, the hint says the address can't be verified instead. + +## Change your photo + +Select **Change photo** and choose an image. It uploads right away and replaces your initials across the app. Images up to 10 MB work; the site usually shrinks them to fit 800 × 800 pixels. Select **Remove** to go back to your initials. + +## Change your password + - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. - - - If you haven't created an account yet, follow the guide on [Creating an account](/guides/creating-an-account). - - + + In **Sign-in & security**, next to **Password**, select **Change**. - - Open Settings from your avatar and choose Account. Your name, username, email and photo are under **Profile**, and each change saves when you leave the field. - - - - On the Profile page, you can update the following information: - - - **Name**: Your full name as it appears in your account - - **Username**: Your unique username (used in public resume URLs) - - **Email Address**: Your account email address - - - If you update your email address, you will receive a verification link on your current email address. The change will only be accepted after you click on the verification link. - - + + Fill in **Current Password** and **New Password**. The new password must be different from the current one and at least 8 characters long. - - If you've updated your email address, check your current email inbox for a verification link. Click on the link to confirm the email change. - - - Check your spam folder if the verification email isn't in your inbox. The email address change only completes after you click the link. - - + + A message confirms the change. You stay signed in on this device. + + + The Update your password dialog with Current Password and New Password fields and an Update Password button + + +If you signed up with a social account, the **Password** row reads **You sign in without one**, and the button says **Set a password**. It takes you to the password reset page, where you can have a link emailed to you to create one. + +## Related guides + +- [Linking social accounts](/guides/linking-social-accounts): sign in with Google, GitHub or LinkedIn. +- [Changing appearance and language](/guides/changing-appearance-and-language): theme and interface language. +- [Filling in your details](/guides/filling-in-your-details): the name, photo and contact details on a resume. diff --git a/docs/guides/using-ai-agent.mdx b/docs/guides/using-ai-agent.mdx deleted file mode 100644 index 9e56954dd..000000000 --- a/docs/guides/using-ai-agent.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: "Using the AI Agent workspace" -description: "Start an isolated AI draft in the Reactive Resume Agent workspace, chat with the agent, review proposed resume patches, and resume prior threads." ---- - -The AI Agent workspace is where you work with an AI assistant on a resume draft. It keeps the conversation, the tool activity, and a read-only resume preview in one full-screen view. - - - Agent threads edit an AI draft copy of your resume. Your original resume is not changed when you start from an existing resume. - - -## Before you start - -You need at least one AI provider that is tested and enabled in **Settings → AI & developer**. For setup, see [Using artificial intelligence](/guides/using-ai). - -If you self-host Reactive Resume, the agent workspace also requires the server-side agent configuration described in [Self-hosting with Docker](/self-hosting/docker). - -## Open the agent workspace - -From the dashboard sidebar, click **Agents**. - -The agent page always shows your thread sidebar. Use it to continue an existing thread, or click **New thread** to start another one. - -You can also open the agent from the builder dock. When you do this, the current resume is preselected in the new thread setup screen. - - - Press `Cmd/Ctrl+K` from anywhere in the app to open the command palette. Select **Threads** to search existing threads by title, resume, or provider, or run **New Thread** to jump straight to the setup screen. - - -## Start a new thread - - - - Pick the provider/model combo the agent should use. This choice is locked once the thread starts. - - - - Select an existing resume to duplicate as an AI draft, or choose **Create from scratch** for a blank draft. - - - - Click **Start Thread** to create the draft and open the workspace. - - - AI Agent new thread setup with model and resume selectors - - - - -## Use the three-pane workspace - -The desktop workspace is split into three panes: - -- **Threads** on the left: continue, archive, delete, or start agent threads. -- **Chat** in the center: send prompts, upload files, answer agent questions, and review tool activity. -- **Resume** on the right: read the current AI draft, adjust zoom, open it in the builder, or download a PDF. - - - AI Agent workspace showing thread sidebar, chat, and resume preview - - -On smaller screens, the workspace uses tabs/sheets so you can switch between threads, chat, and preview without losing the active conversation. - -## Ask for resume changes - -The agent works best with concrete instructions: - -- "Tailor this resume to this job description: `https://example.com/job`" -- "Find weak bullets and rewrite them with stronger outcomes." -- "Compare this draft against a product manager role and update the keywords." -- "Ask me before changing anything that looks uncertain." - -You can attach files or images from the composer. The agent reads uploaded attachments when they are relevant to your request. - -For supplied text, paste the content directly into the chat. Plain text, Markdown, and JSON attachments are available to the agent as extracted text. Images and supported files such as PDFs are passed directly to the selected provider when it can use them. If an attachment format is unsupported by the selected provider, paste the relevant text instead. - - - Text input is supported. Voice input is not supported in the agent workspace yet. - - -## Tailor a resume to a supplied job description - -Use this controlled workflow to test tailoring without relying on live web research: - - - - In **Settings → AI & developer**, configure a supported AI provider, test it, and make sure it is enabled. Then return to **Agents**. - - - - Start a new thread and select a resume containing sample experience data. The agent creates an isolated AI draft, so the source resume remains unchanged. - - - - Paste this sample job description into the chat: - - ```text - Target role: backend engineer. Required: TypeScript and PostgreSQL. - ``` - - Then ask: "Tailor the existing experience for this role. Do not invent qualifications or experience." - - - - Review the updated draft. To approve patches before they are applied, inspect their JSON, or restore an earlier state, follow [Review edits and patches](#review-edits-and-patches). - - - -This workflow also works when the selected provider/model has no live web search. Supplying the job description gives the agent the context it needs for ordinary resume editing. - -## Review edits and patches - -By default, the agent applies resume patches immediately to the AI draft. Turn on **Review edits** in the thread menu if you want to approve or deny patches before they are applied. Applied patches appear in chat as a small **Patch applied** line. - -Open the line to inspect the raw JSON Patch and use **Restore** if you want to roll the draft back to the state before that patch. Restoring an older patch also rolls back patches applied after it. - - - AI-generated changes can still be inaccurate. Review the draft in the preview or builder before exporting or sharing it. - - -## Answer agent questions - -If the agent needs a decision, it may show a question card with recommended answers. Click the answer you want to send it back to the agent. - -That happens when your instructions are ambiguous, job context is missing, or a change depends on your preference. - -## Use the resume preview - -The resume pane is read-only. Use the toolbar to: - -- decrease or increase zoom; -- set an exact zoom percentage; -- open the AI draft in the builder; -- download the draft as a PDF. - -Zoom settings are remembered across refreshes. - -## Manage threads - -Threads are ordered by the newest message. Use the thread menu to archive or delete a thread. - -- **Archive** keeps the conversation but makes the thread read-only. -- **Delete** removes the thread conversation and its attachments. The generated resume draft remains in your dashboard. - -Threads can also become read-only if the working resume is deleted, the selected provider is deleted, or the thread is archived. If a provider is disabled or no longer tested, re-enable and test it before sending new messages. diff --git a/docs/guides/using-ai-in-the-builder.mdx b/docs/guides/using-ai-in-the-builder.mdx deleted file mode 100644 index decb641b1..000000000 --- a/docs/guides/using-ai-in-the-builder.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Using AI in the builder" -description: "Chat with the built-in AI assistant in the builder to review proposed resume changes before applying them, and add an optional AI review to the ATS check." ---- - -Reactive Resume includes two AI-assisted builder workflows: - -- The **AI assistant** opens as a side panel in the builder, scoped to the open resume, so you can chat and apply edits inline. -- The **ATS Check** section can add an optional AI review of your writing on top of its deterministic report. - - - These features require a tested and enabled AI provider in **Settings → AI & developer**. For setup, see - [Using artificial intelligence](/guides/using-ai). - - -## Review the writing from the ATS check - -The **ATS Check** section in the right sidebar runs without AI: its checks are deterministic and run in your browser. Once a deep check has produced a report, it offers an optional AI review of the writing: weak phrasing, bullets that describe duties rather than outcomes, and where a rewrite would land better. - -The review sends the text already extracted from your rendered PDF to the provider you pick, and returns no score. See [Using the ATS checker](/guides/using-the-ats-checker). - -## Open the AI assistant - -Click the **Sparkle** button in the builder header, next to the resume name, to open the assistant. - -The assistant opens as a side panel next to your resume. It uses the same chat interface as the AI Agent workspace, but stays scoped to the resume you are editing. Its edits land in the builder immediately, appear in the preview, and are captured in undo history and version history. - -If AI is unavailable, the panel shows a link to **AI & developer** settings so you can configure a provider. - - - The Sparkle button is the in-builder assistant. The chat-bubble icon on the floating dock opens the full **AI Agent** - workspace in a new page, which suits longer, standalone conversations that create their own draft. See [Using the AI - Agent Workspace](/guides/using-ai-agent). - - -## Ask for targeted changes - -The assistant works best when you ask for specific, incremental changes. - -Good prompts: - -- "Rewrite my summary for a senior frontend engineer role." -- "Tighten the bullets in my most recent job." -- "Add measurable impact to my project descriptions." -- "Adapt this resume for the job description below." - -Avoid asking it to rewrite everything at once unless you are prepared to review many changes. - -## Review proposals before applying them - -When the assistant proposes edits, Reactive Resume shows a review card with: - -- a proposal title and summary; -- badges for the proposed operations; -- before and after previews; -- the raw JSON Patch for users who want to inspect the exact operations. - -You can: - -- **Accept** one proposal; -- **Reject** one proposal; -- move between proposals with **Prev** and **Next**; -- **Accept all** or **Reject all** from the split-button menu. - -Accepted edits are saved through the normal update path, so they show up in the preview, in undo history, and in version history. If you change your mind, use `Cmd/Ctrl+Z` or restore an earlier snapshot from the clock menu. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). - - - Review AI proposals before accepting them. AI can introduce wording that is inaccurate, too generic, or not aligned - with your actual experience. - - -## When a proposal cannot be applied - -A proposal can fail if the resume changed after the assistant generated it, if the resume is locked, or if the proposed patch no longer matches the current resume data. - -If that happens, ask the assistant to regenerate the change from the latest version of the resume. diff --git a/docs/guides/using-ai.mdx b/docs/guides/using-ai.mdx index 48816a105..743ce3808 100644 --- a/docs/guides/using-ai.mdx +++ b/docs/guides/using-ai.mdx @@ -1,94 +1,149 @@ --- -title: "Using artificial intelligence" -description: "Configure an OpenAI, Anthropic, Gemini, OpenRouter, or Ollama provider to power AI edits, resume reviews, agent drafts, and PDF imports." +title: "Connecting an AI provider" +description: "Add your own AI provider key in Reactive Resume, test the connection, and choose which provider the assistant and other AI features use." --- -Reactive Resume uses AI providers for features such as AI-assisted resume changes, the optional AI review in the ATS checker, AI agent drafts, and PDF or Word imports. +Reactive Resume doesn't include an AI model of its own. To use the assistant, AI imports, drafting and the other AI features, you connect a provider you already have an account with (such as OpenAI, Anthropic or Google Gemini) using your own API key. Everything else in the app works without AI. -## Open AI provider settings +## Before you start - - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. - - - - Open **Settings** from your avatar and choose **AI & developer**. - - - AI & developer settings showing AI provider configuration - - - +- Get an API key from your provider's dashboard. You pay the provider directly for what you use. +- Know which model you want to use, such as `gpt-4.1` or `claude-3-5-sonnet-latest`. Reactive Resume suggests a default for each provider, but your account may not have access to every model. ## Add a provider -In the **AI Providers** section, fill out the **Add Provider** form. The form is staged: pick a **Provider** and paste an **API Key** first. **Model** and **Base URL** sit behind the **Advanced settings** toggle, and you only need them for gateways, proxies, local providers, or OpenAI-compatible endpoints. - -| Field | Description | -| --- | --- | -| **Label** | A name that helps you recognize the provider, such as `Work OpenAI` or `Personal OpenRouter`. | -| **Provider** | The provider type, such as OpenAI, Anthropic Claude, Google Gemini, Vercel AI Gateway, OpenRouter, Ollama, or OpenAI-compatible. | -| **API Key** | The key Reactive Resume should use when sending AI requests to that provider. | -| **Model** *(advanced)* | The exact model name expected by that provider. | -| **Base URL** *(advanced)* | The provider endpoint. Leave the default unless you use a gateway, proxy, local provider, or OpenAI-compatible endpoint. | - -Click **Save Provider** when the form is complete. The connection is tested as part of the save, so the success or failure status appears right away. - - - AI & developer settings showing a saved and tested AI provider - - - - Treat API keys like passwords. Anyone with a key can use the connected provider account and may incur costs. - - -## Test and enable a provider - -After saving a provider, test it before using it. - - - Reactive Resume sends a small request to verify the provider, model, base URL, and key. + + Select your name at the bottom of the sidebar, choose **Settings**, then choose **AI & developer**. You can also go to `https://rxresu.me/dashboard/settings/ai` directly. Self-hosted instances use their own address. - - - A successful provider is marked **Tested**. A failed provider is marked **Failed** and may show an error message. + + Under **AI providers**, select **Add provider** (the dashed button below your saved providers). + + Pick your provider from the **Provider** list. Reactive Resume fills in that provider's usual **Model** and **Base URL** for you. Paste your key into **API key**, and change the model if you want a different one. - - Turn on **Use** for the tested provider you want Reactive Resume to use. + + Add provider dialog with Provider set to OpenAI, a hidden API key, Model gpt-4.1, Name Personal OpenAI and the default OpenAI base URL + + + + Select **Save and test**. Reactive Resume saves the provider, then sends it a tiny request to check that the key, model and address work. If the test passes, the dialog closes and the provider is ready to use. - - Only tested providers can be used for AI-assisted features. - +If the test fails, the dialog shows the provider's own error message, such as an invalid key or an unknown model. The provider is still saved, so you can fix it later with **Edit** instead of starting again. -## How credentials are stored +### The fields -AI provider credentials are encrypted on the server and are never shown again after saving. The settings page only shows a preview of the saved key. +| Field | What to enter | +| --- | --- | +| **Provider** | The company or service that runs the model. | +| **API key** | The key from your provider's dashboard. Required. | +| **Model** | The model name exactly as your provider writes it. Required. | +| **Name** | A label you'll recognize, such as "Work OpenAI". If you leave it empty, the provider's name is used. | +| **Base URL** | The address requests go to. Leave the default unless you use a gateway, a proxy or an OpenAI-compatible service. | -If provider management is unavailable, your self-hosted deployment may be missing required server configuration. +### Supported providers + +| Provider | Default model | Default base URL | +| --- | --- | --- | +| OpenAI | `gpt-4.1` | `https://api.openai.com/v1` | +| Anthropic Claude | `claude-3-5-sonnet-latest` | `https://api.anthropic.com/v1` | +| Google Gemini | `gemini-2.0-flash` | `https://generativelanguage.googleapis.com/v1beta` | +| Vercel AI Gateway | `openai/gpt-4.1` | `https://ai-gateway.vercel.sh/v3/ai` | +| OpenRouter | `openai/gpt-4.1` | `https://openrouter.ai/api/v1` | +| Mistral AI | `mistral-large-latest` | `https://api.mistral.ai/v1` | +| Cohere | `command-a-03-2025` | `https://api.cohere.com/v2` | +| xAI Grok | `grok-4` | `https://api.x.ai/v1` | +| Groq | `llama-3.3-70b-versatile` | `https://api.groq.com/openai/v1` | +| DeepSeek | `deepseek-chat` | `https://api.deepseek.com/v1` | +| Together.ai | `meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo` | `https://api.together.xyz/v1` | +| Fireworks | `accounts/fireworks/models/llama-v3p3-70b-instruct` | `https://api.fireworks.ai/inference/v1` | +| Cerebras | `llama3.3-70b` | `https://api.cerebras.ai/v1` | +| Perplexity | `sonar-pro` | `https://api.perplexity.ai` | +| Ollama Cloud | `llama3.1` | `https://ollama.com/api` | +| OpenAI-compatible | none, enter your own | none, enter your own | + +Choose **OpenAI-compatible** for any other service that speaks the OpenAI API, such as a company gateway or LiteLLM. It needs both a model and a base URL. + +The base URL must start with `https://` and point to a public address. On the hosted instance, that means a model running on your own computer (for example, a local Ollama) can't be connected. Self-hosters can allow local and `http://` addresses; see [Environment variables](/self-hosting/environment-variables). + +## Test a provider again + +Each saved provider appears as a row under **AI providers**, showing its name, model and the last four characters of the key. The row shows **Off** next to the name when the provider isn't in use. + + + AI providers section with one provider named Work gateway, model gpt-5-mini, and a Test button reading Connected · 40 ms, followed by the Add provider button + + +Select **Test** on a row to check it again. The button changes to **Connected** with the round-trip time (for example, **Connected · 420 ms**), or to **Failed** with the provider's error message underneath. A passed test turns the provider on; a failed test turns it off, so the app never uses a provider that isn't answering. + +## Edit, turn off or delete a provider + +Select **Edit** on a provider's row. + + + Edit dialog for a provider named Work gateway, with an empty API key field reading Leave empty to keep the saved key, the model, name and base URL fields, the Use this provider switch turned on, Delete provider and Save and test + + +- **Change the key, model, name or base URL**, then select **Save and test**. Leave **API key** empty to keep the saved key. Changing the key, model or base URL turns the provider off until the new test passes. +- **Turn it on or off** with **Use this provider**. Only a provider that passed its test can be turned on. A provider that is off stays saved but isn't offered anywhere in the app. +- **Delete it** with **Delete provider**. The provider and its stored key are removed right away, without a confirmation step. - AI provider management requires the server-side services used to encrypt credentials. If the page says provider - management is unavailable, check your deployment configuration before using AI features. + Deleting a provider can't be undone. Assistant conversations that used it become read-only until you pick another model in the conversation. Documents and edits you already accepted aren't affected. -## Where AI is used +## Which provider is used -After a provider is tested and enabled, you can use AI in: +You can save several providers, for example one per account or one per model. -- the optional **AI review** in the ATS Check section of the builder's right sidebar; -- **AI-assisted resume changes** from the builder; -- **Agent** workflows that create isolated AI drafts; -- **PDF and Microsoft Word imports** from the dashboard import dialog. +- The **assistant** starts new conversations with the provider you used most recently. You can switch models from the model menu in the assistant's header at any time. See [Using the assistant](/guides/using-the-assistant). +- **Check → Writing** lets you pick the provider for each review. +- Other AI features (reading Word and PDF files on import, **Improve selected line**, drafting a letter, and reading a pasted job posting) use the oldest provider you added that is turned on. -For the builder workflow, see [Using AI in the builder](/guides/using-ai-in-the-builder). For the dedicated agent workspace, see [Using the AI Agent workspace](/guides/using-ai-agent) and [AI Agent tools](/guides/ai-agent-tools). For AI-assisted imports, see [Importing resumes](/guides/importing-resumes). +## Connect from the assistant instead + +If no provider is connected yet, opening the assistant in the editor shows **Connect an AI provider** with OpenAI, Anthropic and **Other · OpenAI-compatible**. Enter your key and model there and select **Connect**. It saves and tests the provider the same way as settings. For the other providers, use **AI & developer**. + + + Assistant panel showing Connect an AI provider, with OpenAI, Anthropic and Other · OpenAI-compatible options and a link to Settings for more providers + + +## How your key is stored + +Your API key is encrypted on the Reactive Resume server with AES-256-GCM before it's saved, and it's never shown again. Settings only ever show the last four characters. When a feature needs the key, the server decrypts it just long enough to call your provider. + +Self-hosted instances encrypt keys with the `ENCRYPTION_SECRET` environment variable. Until it's set, **AI & developer** shows "AI providers aren't available on this server until ENCRYPTION_SECRET is set." If the secret changes later, saved keys can no longer be decrypted and have to be entered again. + +## What is sent to your provider + +Requests go from the Reactive Resume server to the provider you chose, using your key. Nothing is sent until you use an AI feature, and each feature sends only what it needs: + +| Feature | What is sent | +| --- | --- | +| Assistant | Your message, the open resume or letter (unless you remove it from the message), the job posting and your notes on the linked application (unless you remove the posting), attachments you add, and the earlier messages in the conversation. | +| **Improve selected line** | The line you're improving and the rest of that field. | +| **Check → Writing** | The text of your resume. | +| Importing a PDF or Word file | The file you import. | +| Drafting a letter | The linked resume and the application's job posting. | +| Adding an application | Job posting text you paste in. | + +Your provider handles that data under its own terms and privacy policy. Reactive Resume keeps assistant conversations and their attachments in your account until you delete the conversation. + +## Related guides + + + + Ask for changes and review the edits it proposes. + + + What the assistant can read and do in a conversation. + + + Get an AI review of your wording in Check mode. + + + Turn an existing PDF or Word resume into an editable one. + + diff --git a/docs/guides/using-private-notes.mdx b/docs/guides/using-private-notes.mdx index a329bc8f7..6487bb5fd 100644 --- a/docs/guides/using-private-notes.mdx +++ b/docs/guides/using-private-notes.mdx @@ -1,104 +1,55 @@ --- title: "Using private notes" -description: "Use the private notes section in Reactive Resume to track job applications, company details, and other personal reminders for each resume." +description: "Keep private notes with a resume in Reactive Resume, such as where you sent it and what you changed. Notes never appear on the resume or its public page." --- -## What are private notes? +Every resume has a private notes area for anything you want to remember about it: where you sent it, who the recruiter +was, what you tailored, or links to job postings. Notes stay out of the resume itself. -The **Notes** section stores personal information about one resume. Nothing you write there appears on the resume itself, whether it is viewed publicly or exported as a PDF. - -It is a notebook attached to each resume, where you can jot down anything relevant to your job search. - - - Your notes are stored with that resume's data and are only visible to you when editing the resume. - - -## Where to find it - -In the resume builder, open the **right sidebar** and select **Notes** from the available sections. - - - Screenshot of the Notes section in the right sidebar - - -## Use cases - -Some practical ways to use them: - -### Track job applications - -Keep a record of where you've sent this particular resume: - -- Company names and positions applied for -- Application dates and deadlines -- Recruiter or hiring manager contact information -- Application status (submitted, interviewing, offer, rejected) - -### Save job description links - -Paste links to the original job postings so you can quickly reference them when preparing for interviews or following up. - - - Job postings often come down once a position is filled. Copy the main requirements or responsibilities into your notes - as a backup. - - -### Interview preparation - -Keep notes to help you prepare: - -- Questions you want to ask the interviewer -- Key points to highlight from your experience -- Salary expectations and negotiation notes -- Company research and talking points - -### Version control reminders - -If you maintain multiple versions of your resume, use notes to remind yourself: - -- What makes this version unique -- Which types of roles this resume is tailored for -- What was changed from your base resume - -### Follow-up reminders - -Track your follow-up schedule: - -- When you last followed up with a company -- Next steps and deadlines -- Response notes from recruiters - -## How to use it +## Write notes for a resume - - In the resume builder, click on **Notes** in the right sidebar to expand the section. + + In the editor, select the resume's name at the top left. The document menu opens. - - - Use the rich text editor to write your notes. You can format text with bold, italics, bullet points, and more. - - - - Your notes are saved automatically as you type. There's no need to click a save button. + + Select **Notes**. The Notes dialog opens with the editor ready for typing. + + + Type your notes. The toolbar under the text offers bold, italic, links, bulleted and numbered lists, and clear + formatting, and Markdown shortcuts such as `- ` for a list work too. + + + Notes save automatically as you type. Close the dialog when you're done. -## Privacy guarantee + + The Notes dialog with two paragraphs about sending the resume to Lumen Games and a follow-up date, above a formatting toolbar and a character count + - - Your notes are for your eyes only. They will **never** appear in your exported files, on your public resume URL, or - in a printed version of your resume. - +## Who can see your notes -That makes it safe to store sensitive details: salary expectations, candid thoughts about an opportunity, or reminders you would not want an employer to see. +Notes are for you. They aren't printed on the resume, and they're left out of: -## Tips for effective note-taking +- the page preview and the downloaded PDF +- DOCX and Markdown exports +- your public resume page, for anyone who isn't you -- Use a similar format across all your resumes, so information is easy to find -- Add dates when you note an application submission or a follow-up -- Keep the notes specific to this version of the resume and the roles it targets -- Update them as your job search moves along +Two places do keep your notes, because they hold the whole resume: + +- **JSON export.** The JSON file is a full copy of the resume's data, notes included. Remove them before you share + that file. +- **History.** Every saved version includes the notes as they were, and restoring a version restores its notes too. + +## Notes for applications + +If you're tracking where you applied, each application in **Applications** has its own notes, next to its stage, +contacts and interviews. Use application notes for things about one job, and resume notes for things about this +version of your resume, such as which roles it's tailored for. Cover letters don't have a notes area. + +## Related guides + +- [Managing an application](/guides/managing-an-application): notes, contacts and next steps for one job. +- [Undoing changes and version history](/guides/undoing-changes-and-version-history): name a version when you send the resume. +- [Formatting text](/guides/formatting-text): the text toolbar and Markdown shortcuts. diff --git a/docs/guides/using-the-api.mdx b/docs/guides/using-the-api.mdx index 84c2f5b65..5ec89344c 100644 --- a/docs/guides/using-the-api.mdx +++ b/docs/guides/using-the-api.mdx @@ -1,98 +1,196 @@ --- title: "Using the API" -description: "Create Reactive Resume API keys, authenticate REST requests with bearer tokens, and integrate resume data into your own scripts, apps, or automations." +description: "Create a Reactive Resume API key, send authenticated REST requests to read and edit your resumes, and revoke keys you no longer need." --- +The Reactive Resume REST API lets your own scripts, extensions and automations read and change your documents and job applications. You authenticate with an API key that you create in Settings. This guide walks you through creating a key, making your first request and revoking the key when you're done. + +## Before you start + +- You need a Reactive Resume account. The examples use the hosted instance at `https://rxresu.me`. If you self-host, replace it with your own address. +- An API key acts as you. Anyone who has it can read and edit your documents, so treat it like a password. + +## Create an API key + - - Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials. + + Select your name at the bottom of the sidebar, choose **Settings**, then open **AI & developer**. You can also press ⌘ K (Ctrl K on Windows and Linux) and type "API keys". - - - Open Settings from your avatar and choose AI & developer. API keys are listed under **API keys**. - - - - The endpoints, request format and authentication are described in the [API Reference](https://docs.rxresu.me/api-reference). + + In the **API keys** section, select **New key**. + + Under **What's it for?**, enter a name that reminds you where the key is used, such as "Resume sync script". Under **Expires**, choose **30 days**, **90 days** or **Never**, then select **Create key**. - - Click New key. Fill in: - - - **What's it for?**: A name to help you identify what you use this key for - - **Expires**: 30 days, 90 days or Never - + + New API key dialog with the name Resume sync script entered and 90 days selected under Expires + + + Select **Copy**, store the key somewhere safe (a password manager or your deployment's secret store), then select **Done**. - - The secret key is shown once, right after you create it. Copy it and store it somewhere safe. + + New API key dialog showing the generated key with a Copy button and the warning Copy it now. For your security, it won't be shown again. + - For security reasons, your API key is only displayed once. If you lose it, you must create a new one. + Reactive Resume shows the key only once. If you lose it, revoke it and create a new one. - - - - - To authenticate API requests, include your key in the x-api-key header. - - - If you're self-hosting, replace https://rxresu.me with your instance URL. The API is served under /api/openapi. - - - ```bash - curl "https://rxresu.me/api/openapi/resumes" \ - -H "x-api-key: YOUR_API_KEY" - ``` - - - - - In the API keys table, click Revoke next to a key. - - - A revoked key stops working immediately. Undo in the message that appears brings it back; once the message goes, the key is deleted for good. - - -## Cover-letter REST endpoint migration +Your keys are listed in the **API keys** table with the date each was created, the date it was last used and when it expires. Expired keys disappear from the list and stop working. -Cover-letter REST operations now use `/cover-letters` resource paths and HTTP methods instead of the automatically generated `/coverLetters/*` POST endpoints. This is a **breaking change for REST clients**: the old REST URLs are no longer served and return `404`. There are no compatibility aliases. + + API keys table listing two keys, Claude Desktop that never expires and Resume sync script that expires on 29 Dec 2026, each with a Revoke button + -All paths below are relative to `/api/openapi` on your instance. Authentication with `x-api-key` is unchanged. +## Make your first request -| Previous endpoint | Replacement endpoint | -| --- | --- | -| `POST /coverLetters/list` | `GET /cover-letters` | -| `POST /coverLetters/getById` | `GET /cover-letters/{id}` | -| `POST /coverLetters/create` | `POST /cover-letters` | -| `POST /coverLetters/update` | `PUT /cover-letters/{id}` | -| `POST /coverLetters/refreshStyle` | `POST /cover-letters/{id}/refresh-style` | -| `POST /coverLetters/duplicate` | `POST /cover-letters/{id}/duplicate` | -| `POST /coverLetters/delete` | `DELETE /cover-letters/{id}` | -| `POST /coverLetters/copyEmbedded` | `POST /cover-letters/from-resume` | -| `POST /coverLetters/export` | `GET /cover-letters/{id}/export` | -| `POST /coverLetters/import` | `POST /cover-letters/import` | - -Update request inputs as well as the URL and method: - -- Move `id` from the JSON body into the URL path wherever `{id}` appears. URL-encode the ID as a single path segment. -- For listing, send `search`, `resumeId`, `applicationId`, `limit`, and `offset` as query parameters, not a JSON body. The get-by-ID and export operations also have no request body. -- Keep the remaining inputs as JSON for POST, PUT, and DELETE requests, with `Content-Type: application/json`. In particular, `expectedRevision` remains required in the JSON body for update, refresh-style, and delete; refreshing style also requires `resumeId`. -- Create, copy-from-resume, and import keep their existing JSON inputs. Update still modifies only the supplied fields; it does not replace the entire document. -- Successful operations return `200`, including create and delete. Delete has an empty response body; do not expect `204` or parse a JSON document from it. Other response shapes are unchanged. - -For example, list cover letters with query parameters: +The REST API lives under `/api/openapi` on your instance. Send your key in the `x-api-key` header. ```bash -curl --get "https://rxresu.me/api/openapi/cover-letters" \ - -H "x-api-key: YOUR_API_KEY" \ - --data-urlencode "search=Engineer" \ - --data-urlencode "limit=20" +curl "https://rxresu.me/api/openapi/resumes" \ + -H "x-api-key: YOUR_API_KEY" ``` -If you generate an SDK, regenerate it from your instance's `/api/openapi/spec.json` after upgrading: operation IDs have changed too (for example, `coverLetters.getById` is now `getCoverLetter`). Self-hosted clients should use the spec from the version they are running. +The response is a JSON array of your resumes, without their full content: -This migration applies only to REST under `/api/openapi`. Existing oRPC clients under `/api/rpc` continue to use the same `coverLetters.*` procedure names and RPC protocol; do not apply the REST path or payload changes to them. +```json +[ + { + "id": "01a0efa5-6529-745e-950a-521ef5b8afc7", + "name": "Game Developer Resume", + "slug": "game-developer-resume", + "tags": ["games"], + "isPublic": false, + "showDownloadButtons": true, + "isLocked": false, + "createdAt": "2026-09-30T00:09:49.101Z", + "updatedAt": "2026-09-30T00:09:49.101Z" + } +] +``` + +Fetch one resume with its full data, using an ID from the list: + +```bash +curl "https://rxresu.me/api/openapi/resumes/01a0efa5-6529-745e-950a-521ef5b8afc7" \ + -H "x-api-key: YOUR_API_KEY" +``` + +For request bodies, send JSON with `Content-Type: application/json`. For example, create a resume filled with sample content: + +```bash +curl -X POST "https://rxresu.me/api/openapi/resumes" \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ "name": "Product Designer Resume", "tags": ["design"], "withSampleData": true }' +``` + +The response is the new resume's ID as a JSON string. + + + To change a few fields in a resume without sending the whole document, use [JSON Patch](/guides/using-the-patch-api). + + +## Find the endpoint you need + +Every endpoint, with its parameters, request body and responses, is listed in the **API Reference** tab of these docs. Your own instance also serves the machine-readable OpenAPI document at `/api/openapi/spec.json` (for example `https://rxresu.me/api/openapi/spec.json`). Use the spec from the version you run when you generate a client. + +| Area | Paths | What you can do | +| --- | --- | --- | +| Resumes | `/resumes`, `/resumes/{id}` | List, read, create, import, update, patch, lock, duplicate, move to Trash, set a sharing password, download a PDF, read version history and statistics | +| Cover letters | `/cover-letters` | List, read, create, update, duplicate, export and import letters, restore versions, refresh their design from a resume | +| Documents | `/documents` | Work across resumes and letters: rename, tag, lock, link to an application, move to Trash, restore, delete now | +| Applications | `/applications` | Track job applications: stages, notes, interviews, attached PDFs, bulk changes, import, statistics | +| AI | `/ai-providers`, `/ai`, `/agent` | Manage your AI providers, run AI tools such as resume import from PDF, work with assistant conversations | +| Account | `/auth/account/export`, `/auth/account` | Export your account data, delete your account | + +`GET /api/health` (outside `/api/openapi`) reports whether the instance and its database and storage are healthy. It needs no key. + +## Authentication methods + +The API accepts three kinds of credentials: + +1. `x-api-key: `: an API key from Settings. Use this for scripts and servers. +2. `Authorization: Bearer `: an OAuth access token, which MCP clients get when you connect them with OAuth. See [Using the MCP server](/guides/using-the-mcp-server). +3. The session cookie of a signed-in browser. + +Send one of them. If a request has an `x-api-key` header, the API ignores any bearer token, so a wrong key isn't rescued by a valid token. + +A request without valid credentials gets `401` with the code `UNAUTHORIZED`. + +## Limits + +| Limit | Value | +| --- | --- | +| Requests per API key | 1,000 per hour | +| Changes to one resume, document or application (create, update, patch, lock, duplicate, delete) | 300 per minute | +| PDF downloads of one resume (`GET /resumes/{id}/pdf`) | 5 per minute | +| AI requests | 20 per minute | +| Request body size on Vercel installations | 4.5 MB (see [Large RPC requests](/guides/large-rpc-requests)) | + +When you hit a limit, the API responds with `429`. On a self-hosted installation, `FLAG_DISABLE_API_RATE_LIMIT` turns off the per-key limit together with the sign-in limits; the other limits stay on (see [Environment variables](/self-hosting/environment-variables)). + +## Errors + +Errors come back as JSON with a machine-readable `code`, the HTTP `status` and a `message`. Validation errors also include the failing fields in `data.issues`. + +```json +{ "defined": false, "code": "NOT_FOUND", "status": 404, "message": "Not Found" } +``` + +Branch on `code` rather than on the message text. + +## Revoke a key + + + + Open **Settings**, then **AI & developer**. In the **API keys** table, find the key you want to stop. + + + Select **Revoke**. The key stops working at once, and a message confirms which key you revoked. + + + Message reading Revoked Claude Desktop. Apps using it stop working now, with an Undo link + + + + Select **Undo** in the message to turn the key back on. When the message closes, the key is deleted for good. + + + +## The RPC endpoint + +The web app talks to the server through oRPC at `/api/rpc`, using the same procedures, authorization and validation as the REST API. It uses oRPC's own wire format, and its procedure names follow the app's source code rather than a published contract. For integrations, use the REST API under `/api/openapi`, which has a documented, generated specification. + +## Changes from v5 + +If you built against the v5 API, check these changes: + +- **Structured dates.** Dated entries have a `dates` object (`start`, `end`, `present`). The text fields `period` and `date` are still returned, but the server rewrites them from `dates` on every save, so writing only the text has no effect. See [Using the patch API](/guides/using-the-patch-api#dates). +- **Cover letters are their own documents.** Resumes no longer hold cover letters. Use the `/cover-letters` endpoints. If you send a resume that still contains a cover-letter section (for example an old export), the server saves each letter as a separate cover letter and removes the section from the resume. +- **No cover-letter PDF from resumes.** `GET /resumes/{id}/pdf` only accepts `target=resume` (or no target). Old signed download links that ask for a cover letter return `404`. +- **Delete moves to Trash.** `DELETE /resumes/{id}` and `DELETE /cover-letters/{id}` move the document to Trash for 30 days. Use the `/documents/restore` and `/documents/purge` endpoints to bring it back or delete it at once. +- **Application stages.** The `rejected` stage and the `archived` flag are gone. Close an application with the `closed` stage and a `closedReason` (`not-selected`, `withdrew`, `accepted-other` or `no-response`). +- **Resume versions.** Versions have a `kind` and an optional `name` instead of a free-text label. + +Self-hosters upgrading an installation can read [Upgrading to v6](/self-hosting/upgrading-to-v6). + +## Related guides + + + + Change individual fields of a resume with JSON Patch. + + + Let AI clients such as Claude or Cursor work with your documents. + + + The structure of resume data, for validation and code generation. + + + How large request bodies reach Vercel installations. + + diff --git a/docs/guides/using-the-assistant.mdx b/docs/guides/using-the-assistant.mdx new file mode 100644 index 000000000..4cdae76dc --- /dev/null +++ b/docs/guides/using-the-assistant.mdx @@ -0,0 +1,142 @@ +--- +title: "Using the assistant" +description: "Ask the AI assistant to improve your resume or cover letter, then accept or reject each edit it proposes. Nothing changes until you accept." +--- + +The assistant is an AI chat panel beside your document in the editor. It reads the resume or letter you have open, and the job posting when the document is linked to an application, then suggests changes as edits you accept or reject one at a time. It never changes your document on its own. + +## Before you start + +Connect an AI provider with your own API key. See [Connecting an AI provider](/guides/using-ai). If you open the assistant before connecting one, it offers to connect one on the spot. + +## Open the assistant + +In the editor, select **Assistant** (the sparkle button) in the editor bar, just before **Share**, or press ⌘ J (Ctrl J on Windows and Linux). Press it again, or select **Close the assistant** (×) in the panel, to close it. + + + Resume editor with the Assistant panel open on the right, showing What should we work on? and three suggestions: Find weak bullets, Tighten to one page and Draft a summary + + +On wide screens the assistant opens as a third column. On smaller laptops it takes the place of the left panel, on tablets it slides in as a drawer, and on phones it fills the screen. + +You can also reach it from elsewhere: + +- In the command bar (⌘ K), type a question and choose **Ask the assistant**. Outside the editor, this opens the document you edited last and sends the question there. See [Using the command bar](/guides/using-the-command-bar). +- On an application, **Prepare for next step** opens its linked resume or letter with the assistant and suggestions such as **How well do I fit this role?**, **Draft a follow-up email** and **Prepare me for the interview**. See [Managing an application](/guides/managing-an-application). + +## Ask for a change + + + + A new conversation starts with a few suggestions, such as **Find weak bullets**, **Tighten to one page** and **Draft a summary** for a resume, or **Make the opening stronger** and **Make it shorter** for a letter. When the document is linked to an application, **Tailor to the … posting** appears first. + + Or type in the box (**Ask, or describe a change…**) and press Enter. Use Shift Enter for a new line. + + + The assistant reads your document, then answers in a sentence or two and shows its suggestions as a set of proposed edits. You can keep editing your document while it works. + + + +Specific requests work best: "Rewrite my summary for a senior frontend role", "Make the bullets in my latest job lead with results", or "Shorten this letter to under 250 words". When you only ask a question, such as "Which bullet is the weakest?", the assistant answers without proposing edits. + +## Review proposed edits + +Each proposed edit shows where it lands (for example, **Summary · paragraph 1**), the current text struck through, the new text highlighted, and a short reason. The same change is marked on the page, and the section shows a **proposed** badge, so you can read it in place. + + + Resume editor where the summary on the page shows the old text struck through and the new text below it, with a banner reading 1 proposed edit on this page · nothing changes until you accept, and the matching card in the Assistant panel + + + + Card titled 1 proposed edit, with the location Summary · paragraph 1, the old summary struck through, the shorter new summary highlighted in green, the reason, and Accept and Reject buttons + + +- Select **Accept** to apply an edit, or **Reject** to dismiss it. +- When a set has more than one edit waiting, **Accept all** applies them together. +- With an edit focused, press A to accept, R to reject, and ↑ ↓ to move between edits. + +Accepted edits show **Applied** and the set's title changes to, for example, **1 of 1 applied**. If you change a passage yourself before deciding, its edit shows **Out of date: the text has changed since.** Ask again to get a fresh suggestion. + +The assistant only rewrites what your document already says. When a posting asks for something your document doesn't mention, it asks you first instead of inventing it. + +## Undo an accepted edit + +Accepting shows a toast, **Edit applied**, with **Undo**. You can also press ⌘ Z (Ctrl Z) like any other change, or restore an earlier version from History. An edit you undo becomes pending again in the conversation, so you can accept it later. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). + +## Answer a question + +When the assistant needs a fact or a choice from you, it shows a blue question card with a few answers. Select one, or type your own under **Or in your own words…** and select **Send**. The assistant then continues with your answer. + + + A request to add accessibility work, followed by a blue question card asking whether the user has done accessibility work, with the answers Yes, I have and No, skip it and a box to answer in your own words + + +## Choose what's shared with each message + +Above the message box, chips show what goes with your next message: the document, and the job posting when the document is linked to an application. Once a conversation has started, select the **×** on a chip to leave it out of your next messages. Without the document, the assistant can't read or edit it for that message. The line under the box always says what will be sent and to which provider, and nothing is sent until you press send. + +## Attach files + +Once a conversation has started, select the paperclip in the message box to attach files, such as a job description PDF or your notes. Each file shows as a chip; select its **×** to remove it before sending. + +- Plain text, Markdown and JSON files are read as text (up to the first 40,000 characters). +- Images, PDFs and MP3 or WAV audio are passed to the model directly. Whether the model can use them depends on your provider. +- Other file types are attached but not read. Paste the relevant text instead. +- You can attach up to 10 files per message, each up to 25 MB, and 100 MB per conversation. + + + On self-hosted instances, attachments need S3-compatible or Vercel Blob storage. Local file storage can't hold them. + + +## Search the web + +With an **OpenAI** provider on its default base URL and a model that supports web search (such as `gpt-5`, `gpt-5-mini`, `gpt-4.1` or `o4-mini`), the assistant can look things up, such as a company or a public job page. The conversation shows **Searched the web**, and the reply lists its **Sources** as links. With any other provider or model, the assistant says it can't browse and asks you to paste what it needs. + +## Switch models + +The model chip in the assistant's header shows the model in use. Select it to switch to another connected provider for this conversation, or choose **Connect another…** to add one in settings. + + + Model menu open under the gpt-5-mini chip, listing the provider Work gateway with its model gpt-5-mini and a checkmark, and Connect another… + + +## Stop, retry or copy a conversation + +- **Stop a reply** with the stop button that replaces send while the assistant is working, or press Esc in the message box. If it stopped before proposing anything, you'll see **Stopped. No edits were proposed.** with **Continue**. +- **When the provider returns an error**, your message is kept. Select **Retry**, or **Switch model** to try another provider. +- **Copy transcript** at the end of the conversation copies the messages as plain text, speaker by speaker, without the tool steps. + +A single reply can run for about four minutes. If it hits that limit, the assistant says "Time limit reached. Your progress is saved. Ask me to continue." + +## Past conversations + +Each conversation belongs to one document. Opening the assistant continues that document's latest conversation. Select **New conversation** (the pencil) to start fresh, or **Past conversations** (the clock) to see earlier ones. + + + Past conversations list under This document, showing three conversations titled by their first message, each with its time and outcome such as 1 of 1 edits accepted or no edits + + +Conversations are titled by your first message and show how many edits you accepted. Conversations about your other documents appear under **Other documents**; opening one takes you to that document. To delete a conversation, hover over it, select **×**, then confirm. Its messages and attachments are deleted, but edits you accepted stay in your document. + +A conversation becomes read-only if its document is locked or deleted, or its provider was deleted. Unlock the document, or pick another model from the model chip, to continue. + + + Replies stream while the conversation is open. On instances without Redis (some self-hosted setups), a reply that's still running when you reload the page can't be picked up again; its finished messages are still saved. + + +## Related guides + + + + Add, test and manage the providers the assistant uses. + + + Everything the assistant can read and do. + + + Link a resume to an application so the assistant sees the posting. + + + Use the assistant on letters, too. + + diff --git a/docs/guides/using-the-ats-checker.mdx b/docs/guides/using-the-ats-checker.mdx index 774f870f3..83b03b8f7 100644 --- a/docs/guides/using-the-ats-checker.mdx +++ b/docs/guides/using-the-ats-checker.mdx @@ -1,55 +1,101 @@ --- -title: "Using the ATS checker" -description: "Check whether an applicant tracking system can read your resume PDF, from the free public page or from inside the builder, without uploading the file anywhere." +title: "Checking any resume PDF with the ATS checker" +description: "Find out whether applicant tracking software can read your resume PDF. Free, no account needed, and the file never leaves your browser." --- -The ATS checker answers one question: **can software read your resume?** +The ATS checker answers one question: can software read your resume? It opens a PDF the way an applicant tracking system (ATS) does, pulls out the text, and shows you what survived and what didn't. It works with a PDF made in any tool, not only Reactive Resume. -It opens your PDF the way a parser would, pulls the text out, and reports what survived and what did not. Everything runs in your browser. The file is never uploaded, nothing is stored, and no account is needed. +The check runs entirely in your browser. The file isn't uploaded or stored, and you don't need an account. - - Use it at [rxresu.me/ats-checker](https://rxresu.me/ats-checker), or from **Check** in the editor. - +## Before you start -## What it measures +- Have your resume as a PDF, up to 25 MB. Password-protected PDFs can't be checked; save an unprotected copy first. +- Optionally, copy the text of a job posting you're applying for. -- Whether the file carries real text, or is a picture of one. -- Whether the text extracts in the order a person reads it, which is where multi-column layouts usually fail. -- Whether your name, email, phone number, links, and dates survive extraction intact. -- Whether the conventional sections are present and can be told apart. -- Whether the file itself is readable: fonts, encryption, forms, size, page geometry. +## Check your PDF -Optionally, paste a job description and it will tell you which of the posting's terms already appear in your resume. + + + Go to [rxresu.me/ats-checker](https://rxresu.me/ats-checker). If you host Reactive Resume yourself, add `/ats-checker` to your own address. + + + Paste the posting into **Job posting**. The report then shows which of its terms your resume already has. Do this before you add the file. + + + Drag the file onto the box, or select **Drop a PDF here or choose a file** to pick it. To see how the checker works first, select **Check a sample file**. -## What it does not measure + + Drop zone reading Drop a PDF here or choose a file, Up to 25 MB, checked in your browser, never uploaded, with a Check a sample file button and an empty Job posting box below + + + + The checker shows its progress: **Opening the file**, **Reading the text**, then **Checking layout and content**. This usually takes a few seconds. + + - - No tool can tell you whether an application will be rejected. That depends on the role, the screening questions, and - the person reading. Anything that claims otherwise is guessing. - +## Read the report -The checker deliberately does not: +The report has the score and issues on the left and your file on the right. -- repeat the widely quoted claim that most resumes are discarded automatically, because that figure comes from marketing copy rather than research; -- enforce a one-page rule; -- treat a font choice, a photo, or an employment gap as a defect. + + ATS checker results page with a score of 88, Reads cleanly, issue rows for Readability, Layout and Terms from the posting, a Fix these in the editor button, and the sample resume PDF on the right + -Employment gaps, writing style, and length are reported as **unscored tips**. They never move the score. +**The score.** A number from 0 to 100 with a verdict: **Reads cleanly** (80 and above), **Mostly readable** (50 to 79) or **Hard for software to read** (below 50). It combines five categories, and a serious problem (a blocker) caps it: a PDF with no real text can't score well because its margins are tidy. The score reflects how reliably text comes out of the file, not your chances of getting the job. -## Reading the report +**The issue rows.** Each category with something to fix gets a row, worst first. Open a row to see each problem in bold, followed by what to do about it. If you pasted a posting, the last row, **Terms from the posting**, counts the terms found (for example 4/14) and lists the missing ones. -**A score from 0 to 100.** It is the weighted result of five categories: readability, layout, sections, contact details, and dates. A blocking problem puts a hard ceiling on it: a file with no text layer cannot score well just because its margins are tidy. The score reflects how reliably text is extracted, not your chances. + + Score of 88 with the Readability row open, listing a ligature problem and icon-font glyphs with their fixes, then Layout and Terms from the posting rows and the Fix these in a few minutes card + -**What could cost you a match.** Each category with something to fix opens to say what went wrong and what to do about it. If you pasted a job posting, a last row counts the posting's terms your resume already has and lists the missing ones. +The five scored categories are: -**Your file, two ways.** **Original page** shows the PDF as a person sees it. **As software reads it** shows the text a parser extracts, in the order it comes out, so you can see a sidebar landing in the middle of your experience or icons turning into boxes. +| Category | What it checks | +| --- | --- | +| **Readability** | Whether software can recover the words at all: a real text layer rather than a picture, fonts that map to letters, no hidden text, encryption or form fields in the way, and a sensible file and page size. | +| **Layout** | Whether the page geometry keeps the reading order: columns, tables, lines stored out of order, tight spacing, very small text and narrow margins. | +| **Sections** | Whether the resume is split into sections software expects. | +| **Contact details** | Whether a recruiter can reach you: name, email, phone and links survive intact. | +| **Dates** | Whether your timeline can be reconstructed from the dates. | -## Fixing the issues +The checker's start page also lists **Writing**: advice for the person reading your resume, such as numbers that show impact, first-person pronouns and long bullets. It never affects the score, and the results page doesn't show it. To see that advice, import the file (see below) and use **Also check the exported PDF** in [Check](/guides/checking-your-resume#check-the-exported-pdf). -**Fix these in the editor** imports the same file into a free Reactive Resume and opens it in **Check**, with the same issues waiting there. If you aren't signed in, you create an account first; the file waits in your browser meanwhile and is imported as soon as you're back. It's read in your browser, as the check was. +Only the first 30 pages of a long file are checked; the report says so when that happens. -## Checking from the editor +## See your file as software reads it -**Check** in the editor runs the same kind of checks as you type, against your resume rather than a file, and pins each issue to its line. **Deep check** renders your resume to a PDF in your browser and runs the full file check against those bytes, which is the same file a recruiter would receive. +Above your file, switch between two views: -For a review of the writing itself, use **Check → Writing**, which asks your own AI provider for suggested rewrites. See [Using Artificial Intelligence](/guides/using-ai). +- **Original page** shows the PDF as a person sees it. +- **As software reads it** shows the plain text a parser extracts, in the order it comes out. + +Compare the two. Look for a sidebar landing in the middle of your experience, contact details split across lines, or icons turning into empty boxes. + + + Plain text extracted from the sample resume in a monospace font, with empty box characters where the section icons were + + +## Fix the issues in Reactive Resume + +Select **Fix these in the editor** to import the same file into a free Reactive Resume account. The file is read in your browser, the same way the check read it, and the new resume opens in **Check** with the issues waiting for you. + +- **If you're signed in,** the import starts straight away. +- **If you aren't,** you're taken to create an account first. The file waits in your browser in the meantime and is imported as soon as you're back, then removed from the browser. + +To start over with a different file, select **Check another file**. + + + No tool can tell you whether an application will be rejected. That depends on the employer's system, the screening questions and the person reading. The checker measures one thing: how faithfully software can extract your file's text. + + +## Related guides + + + + Check mode in the editor: live checks, job match and a writing review. + + + Bring an existing resume into Reactive Resume from a PDF, Word or JSON file. + + diff --git a/docs/guides/using-the-builder-dock.mdx b/docs/guides/using-the-builder-dock.mdx deleted file mode 100644 index b53d49c1d..000000000 --- a/docs/guides/using-the-builder-dock.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Using the builder dock" -description: "Use the builder dock for undo and redo, zoom controls, page stacking, opening the AI Agent, and copying the public resume URL." ---- - -The builder dock is the floating toolbar at the bottom of the resume builder. It holds undo and redo, preview controls, the AI Agent workspace, and the resume's public URL, so you can reach them without leaving the canvas. - - - Resume builder showing the floating dock with undo, redo, zoom controls, page stacking toggle, AI agent and copy URL buttons - - -## Undo and redo - -The dock starts with **Undo** and **Redo** buttons. They apply to every change in the builder: typing, drag-and-drop, template switches, layout changes, and AI edits. - -You can also use keyboard shortcuts: - -| Shortcut | Action | -| --- | --- | -| `Cmd/Ctrl+Z` | Undo the last change | -| `Cmd/Ctrl+Shift+Z` | Redo the last undone change | - -Rapid typing collapses into a single undo step, so one undo removes a phrase rather than one letter. - - - When your cursor is inside a text field, the shortcut falls back to your browser's native input undo. Click outside the - field, or use the dock buttons, to undo builder-wide changes. - - -For longer-range recovery, such as jumping back to a template switch, an import, or an AI edit, see [Undoing changes and version history](/guides/undoing-changes-and-version-history). - -## Zoom controls - -Use the zoom controls to adjust the preview. - -| Control | What it does | -| --- | --- | -| **Zoom out** | Decreases the preview zoom. | -| **Zoom level (%)** | Shows the current zoom. Opens a menu with **Actual size (100%)** and **Fit to view**. | -| **Zoom in** | Increases the preview zoom. | - -Press `Cmd/Ctrl+0` to reset the zoom to fit. - -Zoom only affects the editor preview. It does not change the exported PDF, DOCX, or JSON file. - -## Toggle page stacking - -Use the page-stacking button to switch between stacked pages (vertical) and side-by-side pages (horizontal). This only affects the editor preview. - -## Open the AI Agent - -Click the chat button to open the **AI Agent** workspace in a new page, pre-scoped to the current resume. The agent suits larger, standalone conversations that create their own draft. - -For chatting about the open resume without leaving the builder, use the AI assistant in the builder header instead. See [Using AI in the builder](/guides/using-ai-in-the-builder). - -## Copy the public URL - -Click **Copy URL** to copy the resume's public URL. - -The URL is based on your username and the resume slug: - -```txt -https://rxresu.me/{username}/{slug} -``` - - - Copying the URL does not make the resume public by itself. To allow visitors to open the link, enable public access in - the **Sharing** section of the right sidebar. - - -## Downloading your resume - -Downloads are no longer on the dock. Use the primary **Download PDF** button in the builder header, or open the dropdown next to it for **DOCX**, **JSON**, and **Print**. See [Exporting your resume](/guides/exporting-your-resume). diff --git a/docs/guides/using-the-command-bar.mdx b/docs/guides/using-the-command-bar.mdx new file mode 100644 index 000000000..c6ff564f6 --- /dev/null +++ b/docs/guides/using-the-command-bar.mdx @@ -0,0 +1,91 @@ +--- +title: "Using the command bar" +description: "Open the command bar with ⌘K to search your resumes and applications, jump to any page, change theme or language, or ask the assistant a question." +--- + +The command bar is a search box that runs commands. Use it to open a resume or application by name, jump to any page, +switch the theme or language, or send a question straight to the assistant, without reaching for the mouse. + + + The command bar with the placeholder "Type a command or search…" and three groups: Search for… with Resumes, Applications and Assistant conversations; Preferences with Change theme to… and Change language to…; and Go to… with Home, Documents and New document. + + +## Open the command bar + +Press ⌘ K on a Mac or Ctrl K on Windows and Linux. It works on every page, +including inside the editor. On **Documents**, **Applications** and **Settings**, you can also select **Search or +run…** at the top of the sidebar. + +To close it, press Esc or press ⌘ K again. + +## Run a command + + + + Start typing to filter the list. For example, type "theme" to find **Change theme to…**, or "trash" to find + **Trash**. + + + Use ↑ and ↓ to move, then press Enter, or select the row with the mouse. + + + Some rows open a second list, such as your resumes or the list of languages. To get back to the first list, press + Esc to close the command bar, then open it again. + + + +## What you can do + +The command bar groups its rows by what they do. Rows that need an account appear or become available once you're +signed in. + +### Search for… + +| Row | What it does | +| --- | --- | +| **Resumes** | Lists your resumes, most recently edited first. Type to filter by name, then press Enter to open one in the editor. The first row, **Create a new resume**, opens the **New document** dialog. | +| **Applications** | Lists your job applications. Type a company or role to filter, then open one to see its details. **New Application** starts a new one. | +| **Assistant conversations** | Lists your past conversations with the assistant. Opening one takes you to its document with the conversation showing. | + +### Preferences + +| Row | What it does | +| --- | --- | +| **Change theme to…** | Choose **Light theme**, **Dark theme** or **Match system theme**. | +| **Change language to…** | Choose one of the 55 languages the app is available in. | + +### Go to… + +| Row | Where it goes | +| --- | --- | +| **Home** | The Reactive Resume home page. | +| **Documents** | Your resumes and cover letters. | +| **New document** | Opens the **New document** dialog. | +| **Trash** | Documents you moved to Trash. | +| **ATS Checker** | The public [ATS checker](/guides/using-the-ats-checker) for any PDF resume. | +| **Applications** | Your job application tracker. | +| **New Application** | Opens the dialog to add an application. | +| **Settings** | Opens a second list with **Account**, **Preferences** and **AI & developer**. | + +## Ask the assistant + +When you're signed in, anything you type also appears as a last row: **Ask the assistant "…"**, with your text in the +quotes. Choosing it sends your text to the [assistant](/guides/using-the-assistant). + + + The command bar with "Make my summary shorter" typed in the search box and a single row under Ask: Ask the assistant "Make my summary shorter". + + +- **In the editor**, the question is about the document you have open. The assistant panel opens and sends it. +- **Anywhere else**, Reactive Resume opens the document you edited most recently, opens the assistant and sends your + question there. If you don't have any documents yet, it asks you to create a resume first. + +The assistant needs an AI provider. If you haven't connected one, the assistant panel helps you set one up first. See +[Connecting an AI provider](/guides/using-ai). + +## Related guides + +- [Keyboard shortcuts](/guides/keyboard-shortcuts): the shortcuts for Documents, the editor and the assistant. +- [Managing documents](/guides/managing-documents): search, sort and organize your documents from their own page. +- [Changing appearance and language](/guides/changing-appearance-and-language): the same theme and language choices + in Settings. diff --git a/docs/guides/using-the-mcp-server.mdx b/docs/guides/using-the-mcp-server.mdx index fed6c8304..7ed2a4ab0 100644 --- a/docs/guides/using-the-mcp-server.mdx +++ b/docs/guides/using-the-mcp-server.mdx @@ -1,379 +1,249 @@ --- title: "Using the MCP server" -description: "Connect Reactive Resume to AI tools like Claude Desktop, Cursor, and Codex through the Model Context Protocol to edit and manage resumes via chat." +description: "Connect Claude, Cursor, Codex or any MCP client to Reactive Resume with OAuth or an API key, and see every tool, prompt and resource it offers." --- -The Reactive Resume MCP server lets you manage your resumes and job applications from any MCP-compatible AI tool: Claude Desktop, Cursor, Codex, and others. It connects to the Reactive Resume API and exposes tools for resume editing, Application Tracker workflows, and AI-assisted job application tasks, driven by natural language. +Reactive Resume runs a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server, so AI clients such as Claude, Cursor and Codex can read and edit your resumes, cover letters and job applications when you ask them to in plain language. This guide shows you how to connect a client and lists everything the server offers. -## What is MCP? +## Before you start -The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standard that lets LLM-powered tools connect to external services. Instead of being limited to the built-in chat UI, you can use any MCP client to interact with your resumes. +- You need a Reactive Resume account. +- Your MCP client must support remote servers over Streamable HTTP, or be able to run the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge. +- Decide how the client signs in: + - **OAuth (recommended).** You approve the client in your browser. No secret is copied anywhere. + - **API key.** For clients that can't do OAuth but can send a custom header. Create a key first, as described in [Using the API](/guides/using-the-api). -## Prerequisites +## Find your server address + +The server address is your instance's address followed by `/mcp`. On the hosted instance it's `https://rxresu.me/mcp`. You can copy it from **Settings** → **AI & developer**, in the **MCP server** section. + + + MCP server section showing the address https://rxresu.me/mcp with a Copy button and a Setup guide link + + +## Connect with OAuth - - Reactive Resume MCP supports two authentication methods: - - - **OAuth2 (recommended):** best user experience for clients that support MCP OAuth. - - **API key (fallback):** works in all clients that can send custom headers. - - Use OAuth2 whenever your MCP client supports it. Use API key only when OAuth is unavailable in that client. - + + Add a remote MCP server with the address `https://rxresu.me/mcp` and no headers. Client-specific steps are in [Set up popular clients](#set-up-popular-clients). + + Your client opens a browser window. If you aren't signed in, Reactive Resume asks you to sign in first. + + + Check the application name under **Connect an application**, read what it will be able to do, and select **Allow access**. Select **Deny** if you don't recognize the application. - - Head over to [https://rxresu.me](https://rxresu.me) (or your self-hosted instance), sign in, and navigate to **Settings → AI & developer**. Under **API keys**, click **New key**, give it a name, and copy the secret. It is shown only once. - - For the full walkthrough, see [Using the API](/guides/using-the-api). - + + Connect an application screen for a client named Claude, listing access to resumes and job applications, profile information, email address and offline access, with Deny and Allow access buttons + + + + The browser hands control back to your client, which can now use the Reactive Resume tools. -## Configuration +## Connect with an API key -There are two transport options, and each can use either OAuth2 or API key depending on your client capabilities. - -### Method 1: Streamable HTTP (recommended) - -If your client supports the `url` field (e.g. **Cursor**, **Codex**, Claude custom connectors), use this. - -#### Option A: OAuth2 (recommended) - -Most OAuth-capable clients only need the MCP URL: - -```json -{ - "mcpServers": { - "reactive-resume": { - "url": "https://rxresu.me/mcp" - } - } -} -``` - -Then connect/sign in from the client UI (or with the client's OAuth login command). - -#### Option B: API key (fallback) - -If OAuth is not supported in your client, send `x-api-key`: +If your client can't do OAuth, send your API key in the `x-api-key` header: ```json { "mcpServers": { "reactive-resume": { "url": "https://rxresu.me/mcp", - "headers": { - "x-api-key": "your-api-key" - } + "headers": { "x-api-key": "YOUR_API_KEY" } } } } ``` -### Method 2: mcp-remote - -If your client only supports `command` / `args` (for example, local-only Claude Desktop config), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**. - -`mcp-remote` is most commonly used with API keys: +If your client only runs local commands, use `mcp-remote` as a bridge. It needs a current version of [Node.js](https://nodejs.org): ```json { "mcpServers": { "reactive-resume": { "command": "npx", - "args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"] + "args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:YOUR_API_KEY"] } } } ``` -Replace `your-api-key` with the API key you created in the prerequisites step. + + An API key in a config file gives full access to your documents. Keep the file private, and revoke the key in Settings if it leaks. + -### Where to put the config +## Set up popular clients -| Client | Config file | -| ----------------- | ------------------------------------------------------------------------------------------------ | -| Cursor | `.cursor/mcp.json` in your project or home directory | -| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/quickstart/user)) | -| Codex | `~/.codex/config.toml` or `.codex/config.toml` ([docs](https://developers.openai.com/codex/mcp)) | -| Other MCP clients | Refer to the client's documentation | + + + Add a custom connector with the URL `https://rxresu.me/mcp`, then connect it and approve access in the browser. See Anthropic's guide to [custom connectors](https://claude.com/docs/connectors/custom/remote-mcp). + + + ```bash + claude mcp add --transport http reactive-resume https://rxresu.me/mcp + ``` -## Authentication details (how Reactive Resume MCP works) + Then run `/mcp` inside Claude Code and choose **reactive-resume** to sign in. + + + Add the server to `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` for all projects: -Reactive Resume MCP accepts authentication in this order: - -1. **Bearer token (OAuth2 access token)** via `Authorization: Bearer ` -2. **API key fallback** via `x-api-key: ` - -If neither is valid, the MCP endpoint responds with `401` and advertises OAuth metadata using: - -- `WWW-Authenticate: Bearer resource_metadata="/.well-known/oauth-protected-resource"` - -OAuth-capable MCP clients use this to discover and complete the OAuth flow automatically. - -### OAuth2 flow used by this server - -Reactive Resume is configured as an OAuth authorization server for MCP clients: - -- The MCP endpoint is `https://rxresu.me/mcp`. -- OAuth discovery metadata is exposed under `/.well-known/*` endpoints. -- The login/authorization route is `/api/auth/oauth`. -- If the user is not signed in, `/api/auth/oauth` redirects to `/auth/login`, then resumes OAuth. -- If the user is signed in, `/api/auth/oauth` uses the OAuth provider to validate the signed request, registered redirect URI, scopes, resource grants, and PKCE before issuing an authorization code, and redirects back to the client. -- PKCE parameters (`code_challenge`, `code_challenge_method`) are preserved in the authorization flow. - -## Popular client setup - -### Cursor - -**OAuth2 (recommended):** - -```json -{ - "mcpServers": { - "reactive-resume": { - "url": "https://rxresu.me/mcp" - } - } -} -``` - -**API key fallback:** - -```json -{ - "mcpServers": { - "reactive-resume": { - "url": "https://rxresu.me/mcp", - "headers": { - "x-api-key": "your-api-key" + ```json + { + "mcpServers": { + "reactive-resume": { "url": "https://rxresu.me/mcp" } } } - } -} -``` + ``` -### Codex (CLI / IDE extension) + Cursor offers to sign in when it first connects. To use an API key instead, add the `headers` object shown in [Connect with an API key](#connect-with-an-api-key). + + + ```bash + codex mcp add reactive-resume --url https://rxresu.me/mcp + codex mcp login reactive-resume + ``` -Add server: + To use an API key instead, add this to `~/.codex/config.toml`: -```bash -codex mcp add reactive-resume --url https://rxresu.me/mcp -``` + ```toml + [mcp_servers.reactive-resume] + url = "https://rxresu.me/mcp" + http_headers = { "x-api-key" = "YOUR_API_KEY" } + ``` + + + Use the server address with your client's remote MCP (Streamable HTTP) option. If it asks for a transport, choose HTTP. If it can't do OAuth, send the `x-api-key` header. + + -Then sign in with OAuth: + + Self-hosting? Replace `https://rxresu.me` with your own address everywhere on this page, for example `https://resume.example.com/mcp`. + -```bash -codex mcp login reactive-resume -``` +## Try it -API key fallback (`config.toml`): +Ask your client something like: -```toml -[mcp_servers."reactive-resume"] -url = "https://rxresu.me/mcp" -http_headers = { "x-api-key" = "your-api-key" } -``` +- "List my resumes." +- "Change the headline on my Game Developer Resume to Senior Game Developer." +- "Add Unreal Engine 5 to my skills with the level Expert." +- "Make a copy of my Game Developer Resume for a technical designer role." +- "Review my resume and give me a score." (uses the `review_resume` prompt) -### Claude (web app custom connector) +Before it edits, the client reads the resume with `read_resume`, then changes it with `apply_resume_patch`. Every change it makes appears in the resume's history as **AI edit**, so you can restore an earlier version. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). -Add `https://rxresu.me/mcp` as a custom remote MCP connector, then connect with OAuth in Claude's connector UI. +For job applications, see [Managing applications with MCP](/guides/managing-applications-with-mcp). -### Claude Desktop (local config file) +## Tools -Use `mcp-remote` bridge with API key (example shown above in **Method 2**). +The server offers 43 tools. Each carries MCP annotations (`readOnlyHint`, `destructiveHint` and others), so clients can run read-only tools freely and ask you before tools that change or delete data. -## External references +### Resumes -- [Cursor MCP docs](https://cursor.sh/docs/mcp) -- [MCP quickstart for users (Claude Desktop example)](https://modelcontextprotocol.io/quickstart/user) -- [OpenAI Codex MCP docs](https://developers.openai.com/codex/mcp) -- [Claude custom connectors (remote MCP)](https://claude.com/docs/connectors/custom/remote-mcp) -- [MCP Authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization) +| Tool | What it does | +| --- | --- | +| `list_resumes` | Lists your resumes (ID, name, slug, tags, public and locked status, dates). Filter by `tags`, sort by `lastUpdatedAt`, `createdAt` or `name`. | +| `list_resume_tags` | Lists every tag used across your resumes. | +| `read_resume` | Returns a resume's full data. | +| `download_resume_pdf` | Returns a signed PDF download link that expires in 10 minutes. | +| `create_resume` | Creates a resume with a `name` and `slug`, empty or with sample content (`withSampleData`). | +| `import_resume` | Creates a resume from a full resume data object, such as a JSON export. | +| `duplicate_resume` | Copies a resume under a new `name` and `slug`. | +| `apply_resume_patch` | Changes a resume's content with JSON Patch operations. See [Using the patch API](/guides/using-the-patch-api). | +| `update_resume` | Changes the name, slug, tags or public setting, and returns the public address. Passwords can only be set in the app. | +| `delete_resume` | Moves a resume to Trash, where it stays for 30 days. | +| `lock_resume` | Locks a resume so it can't be edited or deleted. | +| `unlock_resume` | Unlocks a resume. | +| `get_resume_statistics` | Returns view and download counts, and when the resume was last viewed and downloaded. | -## Self-hosting +### Cover letters -If you're running a self-hosted Reactive Resume instance, replace `https://rxresu.me/mcp` with your instance URL: +| Tool | What it does | +| --- | --- | +| `list_cover_letters` | Lists your cover letters. Filter by name (`search`), `resumeId` or `applicationId`. | +| `read_cover_letter` | Returns one letter, including its `revision`. | +| `create_cover_letter` | Creates a letter, optionally linked to a resume (for its sender details and design) or an application. | +| `update_cover_letter` | Changes a letter's text, recipient, template or links. Needs the latest `revision` as `expectedRevision`. | +| `refresh_cover_letter_style` | Copies the sender details and design from a resume, keeping the letter's text and template. Needs `expectedRevision`. | +| `duplicate_cover_letter` | Copies a letter. | +| `delete_cover_letter` | Moves a letter to Trash for 30 days. Needs `expectedRevision`. | +| `export_cover_letter` | Returns a letter as versioned cover-letter JSON. | +| `import_cover_letter` | Creates a letter from cover-letter JSON produced by `export_cover_letter`. | -```json -{ - "url": "https://resume.example.com/mcp", - "headers": { - "x-api-key": "your-api-key" - } -} -``` +### Applications -### Reconnecting after the OAuth provider upgrade +| Tool | What it does | +| --- | --- | +| `list_applications` | Lists your applications with contacts, documents and timeline. Filter by stage (`status`) or `tags`. | +| `read_application` | Returns one application in full. | +| `list_application_tags` | Lists every tag used across applications. | +| `get_application_stats` | Counts applications by stage and source. | +| `create_application` | Creates an application. `company` and `role` are required. | +| `update_application` | Changes fields, moves the stage, edits contacts, follow-up and tags, or links a resume and letter. Lists you send replace the old ones. | +| `add_application_note` | Adds a note to the activity timeline. | +| `add_application_interview` | Schedules an interview (`screening`, `technical`, `behavioral`, `onsite` or `other`). | +| `update_application_interview` | Reschedules or edits an interview. | +| `update_application_timeline_entry` | Changes the date of a stage or note entry, or a note's text. | +| `delete_application_timeline_entry` | Deletes a note, an interview or an older stage entry. | +| `delete_application` | Permanently deletes an application and the PDFs uploaded to it. | +| `bulk_update_applications` | Moves several applications to a stage or adds tags to them. | +| `bulk_delete_applications` | Permanently deletes several applications. | +| `import_applications` | Creates up to 500 applications from parsed rows. | +| `attach_application_document` | Attaches a resume or cover-letter PDF (base64, up to 10 MB) as what you sent. | +| `remove_application_document` | Removes an attached PDF. | +| `autofill_application_from_job` | Reads a pasted job posting with your AI provider and suggests company, role, location and salary. | +| `score_application_match` | Scores the linked resume against the job description with your AI provider. | +| `tailor_resume_for_application` | Makes a private copy of the linked resume with a summary rewritten for the job by your AI provider, and links the copy to the application. | +| `draft_application_message` | Drafts a cover letter (saved as a new letter) or a recruiter follow-up with your AI provider. | -Self-hosted instances automatically apply the additive OAuth schema migration at startup. This preserves existing client and token records and adds the provider's resource and client-resource tables. +The last four tools send data to the AI provider you set up in Reactive Resume and need a tested default provider. See [Connecting an AI provider](/guides/using-ai). -Clients registered before OAuth provider 1.7 do not have per-resource grants. Remove the old connection from your MCP client and add it again so it dynamically registers a new client, then sign in again. Refreshing an existing token does not create these grants. New registrations receive only the resources configured for this instance; existing clients are not automatically granted access. +## Prompts -## Available tools +Prompts are ready-made instructions your client can start from. Each takes a resume `id` and includes that resume and the schema. -Tool names use canonical unprefixed `snake_case` names. +| Prompt | What it does | +| --- | --- | +| `build_resume` | Walks you through building a resume section by section. | +| `improve_resume` | Suggests concrete improvements to wording, impact and structure. | +| `review_resume` | Gives a structured critique with a scorecard and prioritized recommendations, without changing anything. | -| Tool | Description | -| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name | -| `list_resume_tags` | List every distinct tag in use across your resumes (sorted) | -| `read_resume` | Get the full data of a specific resume by ID | -| `download_resume_pdf` | Create a 10-minute authenticated PDF download URL for a resume | -| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data | -| `import_resume` | Create a resume from a full ResumeData JSON export (random name/slug). Large files may exceed client limits | -| `duplicate_resume` | Create a copy of an existing resume with a new name and slug | -| `apply_resume_patch` | Apply JSON Patch (RFC 6902) operations to modify a resume's data | -| `update_resume` | Update metadata only: name, slug, tags, `isPublic`. Returns canonical share URL; passwords are not managed via MCP | -| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** | -| `lock_resume` | Lock a resume to prevent edits, patches, and deletion | -| `unlock_resume` | Unlock a previously locked resume to re-enable editing | -| `get_resume_statistics` | Get view and download statistics for a resume | -| `list_cover_letters` | List the account's cover letters, which are documents of their own | -| `read_cover_letter` | Read one independent library cover letter by ID | -| `create_cover_letter` | Create an independent library cover letter, optionally linked to a resume or application | -| `update_cover_letter` | Update an independent cover letter with revision-checked concurrency | -| `refresh_cover_letter_style` | Refresh independent-letter styling from a resume without changing its content or template | -| `duplicate_cover_letter` | Create an independent copy of a library cover letter | -| `delete_cover_letter` | Permanently delete an independent library cover letter; requires its current revision | -| `export_cover_letter` | Export one independent library cover letter as versioned cover-letter JSON | -| `import_cover_letter` | Import versioned cover-letter JSON as a new independent library letter | -| `list_applications` | List tracked job applications. Supports stage, tag, and archived filters | -| `read_application` | Read one full application record with contacts, follow-up details, documents, and timeline | -| `list_application_tags` | List every distinct tag used across applications | -| `get_application_stats` | Get aggregate application counts by stage and source | -| `create_application` | Create a tracked job application | -| `update_application` | Update fields, move stage, archive/unarchive, edit contacts/follow-ups/tags, or link a resume | -| `add_application_note` | Append a note to an application's activity timeline | -| `add_application_interview` | Schedule an interview (screening, technical, behavioral, onsite, other) on an application's timeline and calendar | -| `update_application_interview` | Reschedule or edit an interview's type, duration, location, or notes | -| `delete_application` | Permanently delete one application and its owned uploaded documents | -| `bulk_update_applications` | Move, archive/unarchive, or add tags to multiple applications | -| `bulk_delete_applications` | Permanently delete multiple applications | -| `import_applications` | Bulk-create parsed application rows, up to 500 items | -| `attach_application_document` | Attach a sent resume or cover-letter PDF from base64-encoded PDF bytes | -| `remove_application_document` | Remove a sent resume or cover-letter PDF | -| `autofill_application_from_job` | Use AI to extract job details from a URL or pasted job description | -| `score_application_match` | Score the linked resume against the application job description | -| `tailor_resume_for_application` | Create and link a tailored resume copy for an application | -| `draft_application_message` | Draft a cover letter or recruiter follow-up from application and resume context | +## Resources -### Breaking change (tool names) +| URI | Contents | +| --- | --- | +| `resume://_meta/schema` | The resume data JSON Schema, listed in `resources/list`. Clients use it to build valid patches. | +| `resume://{id}` | One resume's full data as JSON. This is a resource template (`resources/templates/list`); find IDs with `list_resumes`. | -Older clients may refer to prefixed or dot-separated names. Those names are no longer registered; update automations and saved prompts to the canonical names above. +Your instance also publishes a server card at `/.well-known/mcp/server-card.json` that summarizes the tools, prompts and resources, for clients that discover servers without connecting. -### Independent and embedded cover letters +## How authentication works -Reactive Resume has two cover-letter scopes: +This section is for client developers and self-hosters. -- **Independent library letters** live in the **Cover Letters** dashboard and have their own IDs, revisions, templates, exports, and lifecycle. Use `list_cover_letters`, `read_cover_letter`, `create_cover_letter`, `update_cover_letter`, `refresh_cover_letter_style`, `duplicate_cover_letter`, `delete_cover_letter`, `export_cover_letter`, and `import_cover_letter` for this scope. -- **Embedded letters** live as cover-letter items inside a resume's custom sections. They are part of that resume's `ResumeData`; use `read_resume` and `apply_resume_patch` to inspect or edit them. Use `copy_embedded_cover_letter` when you want to create a separate independent library copy. Copying does not remove or change the embedded item. - -Updating or deleting an independent letter requires its latest `revision` as `expectedRevision`. This prevents concurrent MCP clients from overwriting newer edits. - -## Available resources - -Resources follow MCP conventions: **static** items appear in `resources/list`; **parameterized** access is declared in `resources/templates/list` and read via `resources/read` once you know the ID. - -| Discovery | What you get | -| ------------------------------------- | --------------------------------------------------------------------------------------------- | -| `resources/list` | Static resources only: currently **`resume://_meta/schema`** (ResumeData JSON Schema) | -| `resources/templates/list` | **`resume://{id}`**: template for reading full resume JSON by ID (not enumerated per resume) | -| `list_resumes` (tool) | **Primary way to discover resume IDs**; resumes are not listed as separate MCP resources | - -| URI | Description | -| ----------------------- | ------------------------------------------------------------------------ | -| `resume://_meta/schema` | ResumeData JSON Schema; use for valid JSON Patch paths and value types | -| `resume://{id}` | Full resume data as JSON; use an ID from `list_resumes` | - -### Breaking change (schema URI) - -The schema resource was previously `resume://schema`. It is now **`resume://_meta/schema`**. Update any saved prompts, automations, or client configs that referenced the old URI. - -### Static server card (`/.well-known/mcp/server-card.json`) - -`GET /.well-known/mcp/server-card.json` returns a JSON document ([SEP-1649](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1649)) with `serverInfo`, optional authentication metadata, and summaries of tools, resources, resource templates, and prompts. It is generated to match the live MCP server, and a client that cannot run a full capability scan against `/mcp/` can use it for discovery. - -## Available prompts - -Prompts are pre-built workflows that give the AI structured instructions and context. Each prompt embeds the resume data and the schema resource (`resume://_meta/schema`) automatically. - -| Prompt | Description | -| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `build_resume` | Guide you step-by-step through building a resume from scratch: basics, summary, experience, education, skills, and design | -| `improve_resume` | Review your resume and suggest concrete improvements to wording, impact, metrics, and structure | -| `review_resume` | Get a structured, professional critique with a scorecard (1–10 across seven dimensions) and prioritized recommendations. **Read-only**; no changes are made | - -## Usage examples - -Once your MCP client is connected, you can work with your resumes in natural language: - -### Browsing - -- "List my resumes" -- "Show me my resume named 'Software Engineer'" -- "What skills are listed on my resume?" -- "Show me the stats for my resume" - -### Tracking applications - -- "Create an application for Senior Frontend Engineer at Acme, stage saved, source LinkedIn." -- "List my archived applications tagged remote." -- "Move my Acme application to interview and add a note that the technical screen is next Tuesday." -- "Attach this resume PDF to the Acme application." -- "Score the resume linked to this application against the job description." -- "Create a tailored resume copy for this application." -- "Draft a follow-up message for the recruiter." - -For a complete workflow and prompt library, see [Managing applications with MCP](/guides/managing-applications-with-mcp). - -### Creating and managing - -- "Create a new resume called 'Frontend Engineer 2026'" -- "Import this exported ResumeData JSON as a new resume" -- "What tags do I use across my resumes?" -- "Duplicate my 'Software Engineer' resume for a product manager role" -- "Make my resume public and give me the share link" -- "Lock my finalized resume so it can't be accidentally edited" -- "Delete my old draft resume" -- "Download a PDF of my Software Engineer resume" -- "Download the visible cover letter from my Software Engineer resume as a PDF" - -### Editing - -- "Update my name to Jane Doe" -- "Change my headline to Senior Software Engineer" -- "Add TypeScript to my skills with an Advanced proficiency level" -- "Add a new experience entry for my role as Staff Engineer at Acme Corp from Jan 2024 to Present" -- "Remove the third item from my skills section" - -### Styling - -- "Change the template to bronzor" -- "Set the primary color to blue" -- "Hide the interests section" - -### Using prompts - -- "Help me build my resume from scratch" (uses `build_resume`) -- "Review my resume and give me a score" (uses `review_resume`) -- "Improve the wording on my resume" (uses `improve_resume`) - - - The AI reads your current resume with `read_resume` before making changes with `apply_resume_patch`, so it targets the - correct JSON paths. Use `update_resume` for name, slug, tags, and public visibility, not for section content. - +- The server accepts `Authorization: Bearer ` (an OAuth access token) first, then `x-api-key: `. +- A request with neither gets `401` and a `WWW-Authenticate: Bearer resource_metadata="/.well-known/oauth-protected-resource"` header. OAuth clients use it to discover the authorization server. +- Authorization server metadata is at `/.well-known/oauth-authorization-server`. The authorization server supports dynamic client registration and PKCE with `S256`. +- The consent screen always lists API access, plus the scopes the client asked for: `profile`, `email` and `offline_access` (which lets the client refresh its token while you're away). ## Troubleshooting -| Issue | Solution | -| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) | -| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection | -| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → AI & developer** | -| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs | -| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard | -| Connection refused | Check that the URL is correct and the instance is running | -| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later**; upgrade with `nvm use 20` or `nvm alias default 20` | -| "Application documents must be PDF files" | `attach_application_document` only accepts `contentType: "application/pdf"` and base64-encoded PDF bytes | +| Problem | What to do | +| --- | --- | +| "Unauthorized" and no sign-in window | Your client doesn't support MCP OAuth. Connect with an API key. | +| The sign-in window says the request is invalid or has expired | Start the connection again from your client. | +| `401` with an API key | The key is wrong, revoked or expired. Create a new one in **Settings** → **AI & developer**. | +| A tool says the resume is locked | Unlock it in the app, or ask the client to run `unlock_resume`. | +| A tool says an ID wasn't found | Ask the client to list resumes, letters or applications first to get valid IDs. | +| An edit to dates doesn't stick | The client changed the `period` or `date` text. Ask it to write the `dates` object instead. | +| `import_resume` fails on a large file | Your client's message size limit is too small. Import the file in the app instead. | +| A self-hosted connection made before an upgrade stops working | Remove the server from your client and add it again, so it registers afresh and you approve it again. | + +## Related guides + +- [Managing applications with MCP](/guides/managing-applications-with-mcp): prompts for tracking your job search from an AI client. +- [Using the patch API](/guides/using-the-patch-api): how resume edits are expressed. +- [Using the API](/guides/using-the-api): API keys and the REST API. diff --git a/docs/guides/using-the-patch-api.mdx b/docs/guides/using-the-patch-api.mdx index 04b4f1c3a..9366bcc2d 100644 --- a/docs/guides/using-the-patch-api.mdx +++ b/docs/guides/using-the-patch-api.mdx @@ -1,191 +1,230 @@ --- title: "Using the patch API" -description: "Partially update a Reactive Resume with JSON Patch (RFC 6902) operations to add, remove, replace, and move fields without sending the full document." +description: "Change individual fields of a Reactive Resume with JSON Patch (RFC 6902): add, remove, replace and move items, set dates and avoid overwriting edits." --- -The Patch API lets you make small, targeted changes to your resume without sending the entire data object. Instead of replacing the whole resume with a `PUT`, you send a list of **JSON Patch** operations that describe exactly what to change. +The patch endpoint lets you make small, targeted changes to a resume's content without sending the whole document. You send a list of [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) operations, and the server applies them together and returns the updated resume. This is the same mechanism the MCP server's `apply_resume_patch` tool uses. -This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) standard. +## Before you start -## When to use PATCH vs PUT +- Create an API key and try a first request, as described in [Using the API](/guides/using-the-api). +- Find the resume's ID with `GET /api/openapi/resumes`, then fetch its current content with `GET /api/openapi/resumes/{id}`. Paths in your operations point into the `data` object of that response. +- Keep the [JSON resume schema](/guides/json-resume-schema) at hand. It lists every field, its type and which fields a new item needs. -| Use case | Method | -| -------------------------------------------- | --------- | -| Update a single field (e.g., name, headline) | **PATCH** | -| Add or remove an item in a section | **PATCH** | -| Change template, colors, or fonts | **PATCH** | -| Replace the entire resume data at once | **PUT** | +## Choose PATCH or PUT - - The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like - `name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint. - +| You want to | Use | +| --- | --- | +| Change one field, such as the headline or a company name | `PATCH /resumes/{id}` | +| Add, remove or reorder entries in a section | `PATCH /resumes/{id}` | +| Change the template, colors, fonts or layout | `PATCH /resumes/{id}` | +| Rename the resume, change its slug or tags, or make it public | `PUT /resumes/{id}` | +| Replace the whole resume content at once | `PUT /resumes/{id}` with `data` | -## Authentication +PATCH only changes the resume's `data`. The name, slug, tags and public setting sit outside `data`, so change them with `PUT`, which updates only the fields you send. -All requests require your API key in the `x-api-key` header. See [Using the API](/guides/using-the-api) for how to create one. +## Send a patch - - If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`. - - -## Endpoint - -``` -PATCH /api/openapi/resume/{id} +```http +PATCH /api/openapi/resumes/{id} +x-api-key: YOUR_API_KEY +Content-Type: application/json ``` -### Request body - -The resume ID is taken from the URL path, so the request body only requires the `operations` array: +The body holds the operations, and optionally the version of the resume you based them on: ```json { - "operations": [{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }] + "expectedUpdatedAt": "2026-09-30T00:10:08.197Z", + "operations": [ + { "op": "replace", "path": "/basics/headline", "value": "Senior Game Developer" } + ] } ``` -Each operation is an object with the following properties: +| Field | Required | Description | +| --- | --- | --- | +| `operations` | Yes | At least one JSON Patch operation. They run in order. | +| `expectedUpdatedAt` | No | The resume's `updatedAt` when you read it. If the resume has changed since, the patch is rejected with `409`. See [Avoid overwriting other edits](#avoid-overwriting-other-edits). | -| Property | Required | Description | -| -------- | ---------------------------- | ------------------------------------------------------------------------------- | -| `op` | Yes | The operation to perform: `add`, `remove`, `replace`, `move`, `copy`, or `test` | -| `path` | Yes | A JSON Pointer (RFC 6901) to the target location in the resume data | -| `value` | For `add`, `replace`, `test` | The value to use for the operation | -| `from` | For `move`, `copy` | A JSON Pointer to the source location | +Each operation has these properties: + +| Property | Required | Description | +| --- | --- | --- | +| `op` | Yes | `add`, `remove`, `replace`, `move`, `copy` or `test` | +| `path` | Yes | A [JSON Pointer (RFC 6901)](https://datatracker.ietf.org/doc/html/rfc6901) into the resume data, such as `/basics/name` | +| `value` | For `add`, `replace` and `test` | The value to write or compare | +| `from` | For `move` and `copy` | A JSON Pointer to the source | + +A successful patch returns `200` with the full updated resume, including its new `updatedAt`. ## Examples -### Replace a basic field - -Update the resume holder's name and headline: +### Replace basic fields ```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ +curl -X PATCH "https://rxresu.me/api/openapi/resumes/YOUR_RESUME_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "operations": [ - { "op": "replace", "path": "/basics/name", "value": "Jane Doe" }, - { "op": "replace", "path": "/basics/headline", "value": "Senior Software Engineer" } + { "op": "replace", "path": "/basics/name", "value": "David Kowalski" }, + { "op": "replace", "path": "/basics/headline", "value": "Senior Game Developer" } ] }' ``` ### Add an experience entry -Append a new item to the experience section: +A new entry must be a complete item: include every required field of that item type, a unique `id` (a UUID) and `"hidden": false`. Set its dates in `dates` and leave `period` empty; the server fills it in. ```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ +curl -X PATCH "https://rxresu.me/api/openapi/resumes/YOUR_RESUME_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "operations": [ { "op": "add", - "path": "/sections/experience/items/-", + "path": "/sections/experience/items/0", "value": { - "id": "a1b2c3d4-0000-0000-0000-000000000000", + "id": "019a0000-0000-7000-8000-000000000001", "hidden": false, - "company": "Acme Corp", - "position": "Staff Engineer", - "location": "San Francisco, CA", - "period": "Jan 2024 - Present", - "website": { "url": "https://acme.example.com", "label": "Acme Corp" }, - "description": "

Leading the platform team.

" + "company": "Northwind Games", + "position": "Senior Gameplay Engineer", + "location": "Remote", + "period": "", + "dates": { "start": "2024-01", "end": null, "present": true }, + "website": { "url": "", "label": "" }, + "description": "

Leading the combat systems team.

", + "roles": [] } } ] }' ``` - - The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array". To - insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning. - +A numeric index inserts at that position (`0` puts the entry first). The special index `-` appends to the end, as in `/sections/skills/items/-`. -### Remove an item from a section +### Remove an entry -Remove the second skill (index `1`) from the skills section: +Remove the second skill: -```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ - -H "x-api-key: YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "operations": [ - { "op": "remove", "path": "/sections/skills/items/1" } - ] - }' +```json +{ "operations": [{ "op": "remove", "path": "/sections/skills/items/1" }] } ``` -### Update metadata (template, colors, fonts) +### Move an entry within a section -Switch the template and update the primary color: +Move the first experience entry to the third position: -```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ - -H "x-api-key: YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "operations": [ - { "op": "replace", "path": "/metadata/template", "value": "bronzor" }, - { "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" } - ] - }' +```json +{ "operations": [{ "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" }] } ``` -### Test then replace (optimistic concurrency) +### Change the design -The `test` operation checks that a value matches before the rest of the patch runs. If the test fails, the whole patch is rejected, which keeps you from overwriting changes made by another client: +Switch the template and the primary color: -```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ - -H "x-api-key: YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "operations": [ - { "op": "test", "path": "/basics/name", "value": "Albert Einstein" }, - { "op": "replace", "path": "/basics/name", "value": "Jane Doe" } - ] - }' +```json +{ + "operations": [ + { "op": "replace", "path": "/metadata/template", "value": "bronzor" }, + { "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" } + ] +} ``` -If `/basics/name` is not `"Albert Einstein"` at the time of the request, the entire patch will fail with a `400` error and no changes will be applied. +### Hide a section -### Move an item within a section - -Move the first experience item to the third position: - -```bash -curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ - -H "x-api-key: YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "operations": [ - { "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" } - ] - }' +```json +{ "operations": [{ "op": "replace", "path": "/sections/interests/hidden", "value": true }] } ``` -## Error handling +## Dates -| Status | Error Code | Description | -| ------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `400` | `INVALID_PATCH_OPERATIONS` | The operations are structurally invalid, target a non-existent path, or produce resume data that fails schema validation. | -| `401` | `UNAUTHORIZED` | Missing or invalid API key. | -| `404` | `NOT_FOUND` | The resume does not exist or does not belong to the authenticated user. | -| `403` | `RESUME_LOCKED` | The resume is locked and cannot be modified. Unlock it first. | +Experience (and its roles), education, projects and volunteer entries, and awards, certifications and publications, have a structured `dates` object: - - All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the - operations are applied. - +```json +"dates": { "start": "2022-03", "end": null, "present": true } +``` -## Tips +| Field | Value | +| --- | --- | +| `start` | A year (`"2022"`) or a year and month (`"2022-03"`), or `null`. Single-date entries (awards, certifications, publications) use only `start`. | +| `end` | A year or year and month, or `null` while the entry is ongoing or for single dates. | +| `present` | `true` when the entry is ongoing. It prints as "Present" in the resume's language. | +| `raw` | Optional. Original text that couldn't be read exactly, such as "Summer 2016". It prints as written until the dates are edited. | -- Fetch first, then patch. Use `GET /resume/{id}` to inspect the current structure before writing your operations, so you target the correct paths and array indices. -- Use `test` for safety. When you expect a field to hold a specific value, combine `test` + `replace` so you don't overwrite a concurrent change. -- Batch related changes. You can send multiple operations in one request. They are applied in order, so a later operation can depend on an earlier one. -- The `-` index appends. When adding items to arrays, use `-` as the index (e.g., `/sections/skills/items/-`) to append to the end. +**Write `dates`, not the text.** Each entry also has a text field, `period` (ranges) or `date` (single dates). On every save, the server rewrites that text from `dates`, in the resume's language and date format (`/metadata/page/dateFormat`: `short`, `long`, `numeric` or `iso`). A patch that changes only `period` or `date` is overwritten, and the resume keeps its old dates. + +To change an entry's dates, replace the whole object: + +```json +{ + "operations": [ + { + "op": "replace", + "path": "/sections/experience/items/0/dates", + "value": { "start": "2021", "end": "2024-06", "present": false } + } + ] +} +``` + +With the `long` date format, that entry's `period` becomes "2021 – June 2024". An entry sent without `dates` (for example from an older export) gets them read from its text. + +## Avoid overwriting other edits + +Someone might edit the resume in the browser between your read and your write. Two tools protect you: + +**`expectedUpdatedAt`** rejects the whole patch if the resume changed after you read it. Send the `updatedAt` value from your last read: + +```json +{ + "expectedUpdatedAt": "2026-09-30T00:10:08.197Z", + "operations": [{ "op": "replace", "path": "/basics/headline", "value": "Lead Game Developer" }] +} +``` + +If the resume has moved on, you get `409` with the code `RESUME_VERSION_CONFLICT` and the current `updatedAt` in `data`. Read the resume again, rebuild your operations and retry. + +**`test`** checks a value before the other operations run. If it doesn't match, nothing is applied: + +```json +{ + "operations": [ + { "op": "test", "path": "/basics/name", "value": "David Kowalski" }, + { "op": "replace", "path": "/basics/name", "value": "Dave Kowalski" } + ] +} +``` + +## What happens when a patch is applied + +- **All or nothing.** The operations run in order inside one transaction. If any operation fails, or the result doesn't match the resume schema, none of them are saved. +- **Validation.** The patched resume must pass the same validation as any other save. For example, `/metadata/template` must be one of the available templates. Rich-text fields such as `description` and `/summary/content` are HTML strings, so send HTML (`

…

`) rather than plain text or Markdown. +- **History.** Each successful patch saves a version in the resume's history. It appears as **AI edit**, described as "from the assistant or API", and you can restore an earlier version from the editor. See [Undoing changes and version history](/guides/undoing-changes-and-version-history). +- **Cover letters leave the resume.** If a patch adds a section of type `cover-letter`, the server saves its letter as a separate cover letter linked to the resume and removes the section. Manage letters with the `/cover-letters` endpoints instead. +- **Public resumes update at once.** If the resume is public, the change is visible on its public page straight away. + +## Errors + +| Status | Code | Cause | +| --- | --- | --- | +| `400` | `BAD_REQUEST` | The body is malformed, for example `operations` is empty or an operation lacks `value` or `from`. | +| `400` | `INVALID_PATCH_OPERATIONS` | An operation targets a path that doesn't exist, a `test` failed, or the result doesn't match the schema. `data` holds the failing `index`, the `operation` and a `code` such as `TEST_OPERATION_FAILED` or `OPERATION_PATH_UNRESOLVABLE`. | +| `401` | `UNAUTHORIZED` | The API key is missing, revoked or expired. | +| `404` | `NOT_FOUND` | The resume doesn't exist or belongs to someone else. | +| `409` | `RESUME_VERSION_CONFLICT` | The resume changed after `expectedUpdatedAt`. | +| — | `RESUME_LOCKED` | The resume is locked. Unlock it with `POST /resumes/{id}/lock` and `{ "isLocked": false }`, or in the app. | + + + A locked resume currently answers with HTTP status `500` rather than a `4xx` status. Check the `code` field for `RESUME_LOCKED` instead of relying on the status. + + +## Related guides + +- [Using the API](/guides/using-the-api): keys, authentication and the other endpoints. +- [JSON resume schema](/guides/json-resume-schema): every path and value type you can patch. +- [Using the MCP server](/guides/using-the-mcp-server): let an AI client write these patches for you. diff --git a/docs/guides/using-the-trash.mdx b/docs/guides/using-the-trash.mdx new file mode 100644 index 000000000..3e5e9c95e --- /dev/null +++ b/docs/guides/using-the-trash.mdx @@ -0,0 +1,75 @@ +--- +title: "Using the Trash" +description: "Move resumes and cover letters to Trash in Reactive Resume, undo it, restore them within 30 days, or delete them for good." +--- + +When you remove a resume or cover letter, it goes to **Trash** instead of disappearing. It stays there for 30 days, so you can bring it back if you change your mind. + +## Move a document to Trash + +On Documents, open the document's **⋯** menu and choose **Move to Trash**. In the editor, open the document name menu at the top left and choose **Move to Trash**; you return to Documents. + +Reactive Resume doesn't ask you to confirm, because the move can be undone. A message appears at the bottom of the screen. Select **Undo** to put the document back straight away. + + + A message reading One-Page Resume (copy) moved to Trash with an Undo button + + +While a resume is in Trash, its public link stops working. Restoring it brings the resume back as it was. + + + Locked documents can't be moved to Trash. Choose **Unlock** in the document's menu first. + + +## Open the Trash + +Once Trash holds something, a **Trash** item with a count appears at the bottom of the sidebar. Select it to open the Trash page. + + + The Trash item in the sidebar with a count of 1 + + +You can also open the [command bar](/guides/using-the-command-bar) with ⌘ K (Ctrl K on Windows and Linux) and choose **Trash**, or go to `https://rxresu.me/dashboard/trash`. Self-hosters use their own address. + +The Trash page lists each document with its **Type**, linked **Application**, and how many days are left under **Deleted in**. When it's empty, it says **Trash is empty**. + +## Restore a document + + + + On the Trash page, select **⋯** at the end of the document's row. + + + The Trash page listing One-Page Resume (copy) with 30 days left and a menu showing Restore and Delete now + + + + Select **Restore**. The document goes back to Documents with its content, tags and links intact. + + + +## Delete a document for good + +Documents are deleted permanently 30 days after you moved them to Trash. To delete one sooner: + + + + On the Trash page, open the row's **⋯** menu and choose **Delete now…**. + + + Select **Delete now** to confirm, or **Keep in Trash** to change your mind. + + + A dialog asking Delete One-Page Resume (copy) now? It will be deleted for good and can't be restored, with Keep in Trash and Delete now buttons + + + + + + A deleted document can't be restored, and neither can its version history. If you might need it again, download a JSON copy first (see [Exporting your resume](/guides/exporting-your-resume)). + + +## Related guides + +- [Managing your documents](/guides/managing-documents): rename, duplicate and lock documents. +- [Exporting your data](/guides/exporting-your-data): keep a copy of everything in your account. diff --git a/docs/guides/viewing-application-insights.mdx b/docs/guides/viewing-application-insights.mdx new file mode 100644 index 000000000..263b363dd --- /dev/null +++ b/docs/guides/viewing-application-insights.mdx @@ -0,0 +1,52 @@ +--- +title: "Viewing application insights" +description: "See how far your applications get, how often and how fast people reply, whether tailored resumes do better, and export a picture of your job search pipeline." +--- + +The **Insights** view turns your applications into a few simple numbers and charts. Use it to spot where things stall, for example lots of applications but few replies, and to see whether tailoring your resume is paying off. + +To open it, go to **Applications** and select the **Insights** tab. Insights counts all of your applications, including closed ones, whatever you've typed in the search field. + + + Insights appears once at least one application has reached **Applied**. Until then you see a short message instead. + + +## Read the charts + + + Insights view with a How far applications get chart showing 9 applied, 4 screening, 3 interview and 1 offer, tiles for 56% heard back and 8 days median to first reply, and a Tailored vs. base resume summary + + +- **How far applications get**: For each stage from **Applied** to **Offer**, the number of applications that ever reached it. This uses each application's stage history, so an application that reached **Interview** and was later closed still counts for **Applied**, **Screening** and **Interview**. +- **heard back**: The share of sent applications that got a reply. A reply means the application moved on to **Screening** or later, or was closed as **Not selected**. +- **median to first reply**: The typical number of days between sending an application and that first reply. It uses the dates in each application's **Activity**, so correct those dates if you added applications after the fact. +- **Tailored vs. base resume**: Compares applications sent with a resume made for that job (with **Tailor a resume** or **Copy for a job…**) against those sent with another resume. It only counts applications with a linked resume. With fewer than 10 of those, it reminds you the numbers are small. + +## The pipeline picture + +**Where your applications went** draws your pipeline as a flow from **Saved** to **Offer**. The number above each bar is how many of your current, not-closed applications are at that stage or beyond, and the percentage under it is how many made it on from the stage before. The top line shows how many applications you're tracking in total, and how many are closed. + + + Where your applications went panel with an Export PNG button and a dark Job search pipeline chart showing bars for Saved, Applied, Screening, Interview and Offer with conversion percentages and 2 closed + + +Select **Export PNG** to download the picture as `pipeline-flow.png`, a high-resolution image with a small Reactive Resume mark in the corner. It's made for sharing, for example with a mentor or career coach. + + + The pipeline picture counts only applications that aren't closed, while **How far applications get** includes closed ones. That's why their numbers can differ. + + +## Weekly activity and sources + + + Applications over time bar chart for the last 8 weeks, and a Where applications come from chart with LinkedIn 5, Indeed 3, Company website 2 and Referral 1 + + +- **Applications over time**: How many applications you applied for each week over the last 8 weeks. Saved applications count in the week you added them, and closed applications aren't included. +- **Where applications come from**: How many applications have each **Source**, such as LinkedIn or Referral. Fill in the source on each application to make this useful. Applications without a source aren't shown. + +## Related guides + +- [Managing an application](/guides/managing-an-application): fix stage dates and sources so the numbers are right. +- [Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job): make tailored copies to compare against your base resume. +- [Tracking job applications](/guides/tracking-job-applications): the other views. diff --git a/docs/guides/whats-new-in-v6.mdx b/docs/guides/whats-new-in-v6.mdx new file mode 100644 index 000000000..8266cbcec --- /dev/null +++ b/docs/guides/whats-new-in-v6.mdx @@ -0,0 +1,175 @@ +--- +title: "What's new in v6" +description: "A guide for returning users: how Reactive Resume v6 reorganizes documents, the editor, sharing, the assistant, cover letters, applications and settings." +--- + +Reactive Resume v6 is a full redesign. Your resumes, letters and applications carry over, but many things live in new +places. This page explains what changed from v5 and where to find what you used before. + + + The Reactive Resume v6 editor with the Write, Design and Check modes at the top, the Basics card on the left and the rendered resume page on the right. + + +## At a glance + +| In v5 | In v6 | +| --- | --- | +| Separate **Resumes** and **Cover Letters** dashboards | One **Documents** page with **All**, **Resumes** and **Letters** tabs | +| Builder with left and right sidebars and a dock | Editor with three modes: **Write**, **Design** and **Check** | +| Export panel, sharing section and version history | One **Share & export** panel with **Link**, **Download** and **History** tabs | +| **Agents** page and the builder's AI sheet | The **Assistant** panel inside the editor (⌘ J) | +| Cover letters inside resumes, or in their own library | Cover letters are documents of their own | +| Applications table, board and insights | Applications **List**, **Board**, **Insights** and **Calendar** | +| Dates typed as free text | Structured dates: a month and year, or just a year | +| Six settings pages | **Settings** with **Account**, **Preferences** and **AI & developer** | + +## Documents replaces the dashboards + +The **Resumes** and **Cover Letters** pages are now one page, **Documents**. Use the tabs to show everything, only +resumes or only letters, search with /, sort by **Last edited**, and switch between a grid and a list. +Old links to the resume and letter dashboards still open **Documents**. + +Everything you create starts from **New** in the sidebar (or press N). The **New document** dialog offers +**Import a resume**, **Copy a resume for a job**, **Start blank**, **New cover letter instead** and **Try with a sample +resume**. You can also drop a file anywhere on **Documents** to import it. + +Deleting a document now moves it to **Trash**, with an undo. Documents stay in Trash for 30 days, and you can restore +them or delete them for good before then. The **Trash** link appears in the sidebar only while Trash has something in +it. + +Learn more in [Managing documents](/guides/managing-documents) and [Using the Trash](/guides/using-the-trash). + +## The editor has three modes + +The builder's sidebars, dock and panels are replaced by one editor with three modes. Switch with the control at the top +of the editor, or press 1, 2 or 3. + +- **Write** holds your content: the **Basics** card (name, photo, contact details and custom fields), then your + sections in print order. Each section and entry has a **⋯** menu for options such as hiding, duplicating or moving + it. You can also select a block on the page to jump to it in the panel. +- **Design** holds the look: **Template**, **Type**, **Color**, **Page** and **Advanced** (custom fonts, the date + format, custom CSS and more). The template gallery shows all 15 templates filled with your own content. +- **Check** reviews your resume. It lists issues pinned to the lines they belong to and gives a readability score, a + **Job match** against a posting, and an AI **Writing** review. It replaces the ATS section of the old builder; the + public [ATS checker](/guides/using-the-ats-checker) page is still available. + +The document name at the top left opens the document menu: **Rename…**, **Duplicate**, **Lock editing**, **Notes**, +**Details**, **Print** and **Move to Trash**. Private notes and printing moved here. + +Learn more in [Editor overview](/guides/editor-overview) and [Checking your resume](/guides/checking-your-resume). + +## Sharing, downloads and history live in one place + +**Share** in the editor bar opens the **Share & export** panel. It has three tabs: + +- **Link** turns your public link on or off, sets its address, can **Require a password**, and shows views and + downloads for the last 30 days. The public address moved here from the old "edit details" dialog. +- **Download** saves a **PDF**, **Word**, **Markdown** or **JSON** file, with a file name you can change. +- **History** lists earlier versions of your document. You can name a version (for example, the one you sent to a + company), preview it and restore it. Restoring no longer asks for confirmation, because it can be undone: your + state before the restore is kept as a version called **Before restore**. + +**Download PDF** in the editor bar downloads right away, and ⌘ P now downloads the PDF instead of +opening the print dialog. To print, use **Print** in the document menu. + +Learn more in [Sharing your resume publicly](/guides/sharing-your-resume-publicly), +[Exporting your resume](/guides/exporting-your-resume) and +[Undoing changes and version history](/guides/undoing-changes-and-version-history). + +## The Assistant replaces Agents + +The separate **Agents** page is gone. The AI assistant now opens as a panel beside the page in the editor, for resumes +and letters alike. Open it with the sparkle button in the editor bar or ⌘ J. + +The biggest change: the assistant never edits your document directly. It proposes edits, showing the old text struck +through, the new text and why, and you accept or reject each one. It can ask you a clarifying question +when a request is unclear. Your earlier conversations are under **Past conversations** in the panel, and old Agents +links open the right document with the conversation showing. + +The assistant, **Improve** on a line of text, and Check's **Writing** review use an AI provider you connect yourself in +**Settings → AI & developer**. + +Learn more in [Connecting an AI provider](/guides/using-ai) and [Using the assistant](/guides/using-the-assistant). + +## Cover letters are documents of their own + +In v5, a cover letter could be a section inside a resume. In v6, every letter is its own document in **Documents**, +with its own editor (**Write** and **Design** modes), downloads and history. A letter can be linked to a resume, and it +then uses that resume's contact details and design unless you give the letter its own design. + +When your account moved to v6, every cover-letter section in your resumes was saved as a separate letter linked to that +resume, named after the resume and the section. The resumes themselves no longer contain letters. Letters don't have +public links. + +Learn more in [Writing a cover letter](/guides/writing-a-cover-letter). + +## Applications got a redesign + +The job tracker now has four views: **List**, **Board**, **Insights** and **Calendar**. On phones, the list is used +in place of the board and calendar. + +- **Add application** starts from a job link or a pasted posting. +- Each application moves through **Saved**, **Applied**, **Screening**, **Interview** and **Offer**. When it ends, you + close it with a reason instead of marking it rejected or archiving it. Use **Show closed** to see closed ones. +- Selecting an application opens its detail sheet with the stage, the next step (interviews and follow-ups), what you + sent, notes, contacts and activity. +- The **Calendar** view shows your interviews. **Add to calendar** on an interview saves it as an `.ics` file for your + own calendar app. +- From an application, you can tailor a resume or write a letter for that job. + +Learn more in [Tracking job applications](/guides/tracking-job-applications) and +[Tailoring a resume for a job](/guides/tailoring-a-resume-for-a-job). + +## Dates are structured + +Dates used to be free text. Now each date is a month and year (such as Mar 2022) or just a year, and ongoing entries +use **Present**. Because Reactive Resume understands the dates, it can sort entries and check them, and every date on +your resume prints the same way. Choose how they print in **Design → Advanced → Date format**: Mar 2022, March 2022, +03/2022 or 2022-03. + +Your existing dates were converted automatically. Anything that couldn't be read exactly, such as "Summer 2016", still +prints as you wrote it until you edit that date. + +Learn more in [Entering dates](/guides/entering-dates). + +## A new PDF engine that runs in your browser + +The page you see in the editor is the real PDF, rendered in your browser as you type. Downloads are made in your +browser too, so they match the preview exactly and don't wait for a server. The engine runs in the background, so +typing stays responsive even on long resumes. + +## Settings are consolidated + +The six settings pages are now three. To open them, select your name at the bottom of the sidebar, then **Settings**: + +- **Account**: your profile and photo, password, two-step verification, passkeys and linked sign-in methods, exporting + all your data, and deleting your account. **Sign out** is here too. +- **Preferences**: appearance (**Light**, **Dark**, or following your system), language and motion. +- **AI & developer**: AI providers, API keys and the MCP server. + +Old settings links open the matching new page. + +Learn more in [Updating your profile](/guides/updating-your-profile) and +[Changing appearance and language](/guides/changing-appearance-and-language). + +## Faster ways to get around + +Press ⌘ K (Ctrl K on Windows and Linux) anywhere to open the command bar. +Search your resumes, applications and assistant conversations, jump to a page, change the theme or language, or type a +question to ask the assistant. See [Using the command bar](/guides/using-the-command-bar) and the full list of +[keyboard shortcuts](/guides/keyboard-shortcuts). + +## What was removed + +A few v5 features didn't make it into v6: + +- The compact view on the resume dashboard (grid and list remain) and the collapsible sidebar. +- Automatically applied AI edits. Every assistant edit is now a proposal you review. +- Adding a cover letter inside a resume, and the cover-letter tab in a resume's downloads. Use a separate letter. +- Archiving assistant conversations and showing token counts. +- The fit score stored on applications. + +## If you self-host + +Upgrading your own server from v5 to v6 involves database migrations and a few API changes. Follow +[Upgrading to v6](/self-hosting/upgrading-to-v6) before you update. diff --git a/docs/guides/writing-a-cover-letter.mdx b/docs/guides/writing-a-cover-letter.mdx new file mode 100644 index 000000000..646f3baaf --- /dev/null +++ b/docs/guides/writing-a-cover-letter.mdx @@ -0,0 +1,190 @@ +--- +title: "Writing a cover letter" +description: "Create a cover letter as its own document, link it to a resume and a job application, match your resume's design, and download it." +--- + +In Reactive Resume, a cover letter is a document of its own. It sits next to your resumes in **Documents**, under the **Letters** tab. A letter can take your name, contact details and design from one of your resumes, and its recipient from a job application, so you only write the body. + +## Create a letter + +You can start a letter from an application or from **Documents**. + + + + Starting from the job you're applying for is quickest, because the letter is set up for that job. + + + + In **Applications**, select the application to open its details. + + + Under **What you sent**, select **Write a letter**. The button shows only while the application has no letter. + + + The What you sent section of an application, listing the linked Game Developer Resume with a Write a letter button below it + + + + + The new letter is named after the company, for example "Cover letter — Northwind Games". It becomes this application's letter, takes its recipient from the application (the company, and the first contact's name if there is one), and takes your details and design from the application's resume, if it has one. + + + + + In **Documents**, select **New**, or press N. + + + At the bottom of the dialog, select **New cover letter instead**. + + + The New document dialog, with Import a resume, Copy a resume for a job and Start blank, and New cover letter instead at the bottom left + + + + + The new letter, named "Untitled letter", takes its details and design from the resume you edited most recently. You can link an application and change the resume in the editor. + + + +To bring back a letter you downloaded as JSON from Reactive Resume, import the file from the **New** dialog as you would a resume; it comes back as a letter. See [Importing resumes](/guides/importing-resumes). + +## Fill in the letter + +The letter editor looks like the resume editor, with two modes: **Write** and **Design**. The panel on the left holds the letter's details, and the page on the right shows the letter as it will print, updating as you type. + + + The letter editor with For, To and From sections in the left panel and a printed letter from David Kowalski to Priya Shah at Northwind Games on the right + + + + + Under **For**, select **Link an application** to choose the job the letter is for, or **Change** to pick another. Closed applications aren't listed. Linking fills the recipient's company and name. Choose **No application** to unlink it. Without an application, you fill in the recipient yourself. + + + Under **To**, check **Name or team**, **Company** and **Date**. The greeting follows the name: "Dear Priya," for Priya Shah, or a greeting to the hiring team when the name is empty. + + + Under **From**, choose the **Resume** your details come from. With **Use details from** that resume turned on, your name, headline, email, phone and location stay linked: when you change them on the resume, the letter changes too, and tells you the next time you open it. + + Turn the switch off to keep a copy of your details in the letter instead. The copy doesn't change when the resume does. + + + The From section with Game Developer Resume selected and the switch Use details from Game Developer Resume turned on, labelled Linked + + + + Under **Letter**, select **Write it myself** and type the body. The greeting and sign-off are added around it for you. The editor has the same formatting tools as the resume; see [Formatting text](/guides/formatting-text). + + + +Below the body, **Length** counts your words against the 180 to 320 words most recruiters read, and says whether the letter is short, comfortable or getting long. It's a guide, not a rule. + + + The Length section showing 85 words and the note Short and direct, fine if the posting asks for brevity, above the Design section reading Matches the resume (Azurill), Change it in Design + + +Changes save automatically. The status under the letter's name in the editor bar shows **Saving…**, **Saved** or, if something went wrong, **Not saved** with **Retry**. + +## Draft the body with AI + +If you've connected an AI provider, an empty body offers a first draft. See [Connecting an AI provider](/guides/using-ai) to set one up. + + + An empty letter body reading Start typing, or draft from what we know: the Northwind Games posting and your resume, with Draft from the posting and Write it myself buttons + + + + + Select **Draft from the posting** when the letter is linked to an application, or **Draft from your resume** when it only has a resume. The draft uses only what the letter is linked to: the posting, your resume, or both. Select **Stop** to cancel it while it's being written. + + + When the draft is ready, ask for **Shorter** or **More personal** versions, or select **Discard** to drop it. + + + Select **Keep** to put the draft in the body. Until you do, the draft never replaces anything, and you can edit it freely afterwards. + + + +To draft, a letter needs a linked resume or application. You can also open the Assistant with ⌘ J (Ctrl J on Windows and Linux) to rework the letter; see [Using the Assistant](/guides/using-the-assistant). + +## Match your resume's design + +By default, a letter linked to a resume uses the resume's template, type and colors, so the two always look like a pair. When you change the resume's design, the letter follows. + + + + Select **Design** in the editor bar, or press 2. + + + With **Match** turned on, the letter follows the resume. Select **Change the resume's design** to open the resume in **Design**. + + + Matching the resume section with the switch Match Game Developer Resume turned on and a link Change the resume's design + + + Turn **Match** off to give the letter a design of its own. It keeps the current design as a starting point, and **Template**, **Type**, **Color** and **Page** settings appear for the letter alone. Hover over a template to preview it on the page. + + + Design panel with Match turned off, labelled The letter keeps a design of its own, and a Template grid with Azurill selected + + + + +Turn **Match** back on at any time to follow the resume again. A letter with no resume chosen under **From** always has its own design. + +## Download the letter + +Select **Download PDF** in the editor bar, or press ⌘ P (Ctrl P). The PDF includes the header with your name and contact details, and is named after you, for example `David-Kowalski-Cover-Letter.pdf`. + +For other formats, select **Share**, or press ⌘ Shift E (Ctrl Shift E). The **Share & export** sheet opens on **Download**: + + + The Share and export sheet for a letter with Download selected, listing PDF, Resume plus letter (2 files), Word, Markdown and JSON, a File name field and a Download PDF button + + +| Option | What you get | +| --- | --- | +| **PDF** | The letter exactly as it looks on the page. Best for applying. | +| **Resume + letter** | The linked resume and this letter as two PDFs, named to match. Shows only when the letter has a resume. | +| **Word** | A `.docx` file with simplified layout, for portals that ask for Word. | +| **Markdown** | Plain text with headings, to paste into application forms. | +| **JSON** | A backup of the letter that imports back into Reactive Resume as a letter. | + +Edit **File name** if you like, then select the download button. + +Letters have no public link. To share one, download it and attach the file. + +When you download a resume from its own Share sheet and the resume's application has a letter, you can also tick **Also download the … cover letter** (the label names the company) to get both files at once. See [Exporting your resume](/guides/exporting-your-resume). + +## Letters and applications + +- Each application has one letter. If you link a letter to an application that already has one, the new letter takes its place. +- When the application moves to **Applied** or a later stage, the letter as it reads then is saved in its **History** as the version you sent. The application shows it under **What you sent** as "Version sent" with the date. +- In **Documents**, a letter's menu has **Link to application…** to link or unlink it without opening it. + +To see earlier versions of a letter, open **History** in the editor bar (the clock icon) or the **History** tab in **Share & export**. It works like resume history; see [Undoing changes and version history](/guides/undoing-changes-and-version-history). + +## Manage a letter + +Select the letter's name in the editor bar to **Rename…**, **Duplicate**, **Lock editing** or **Move to Trash**. A locked letter can't be edited until you select **Unlock editing**. A letter in Trash can be restored for 30 days; see [Using the trash](/guides/using-the-trash). + + + In earlier versions, a cover letter could be a section inside a resume. Those letters are now letters of their own in **Documents**, named after the resume and the letter (for example "Frontend Resume — Cover Letter"), still linked to the resume's details and design. The resume no longer contains them. These older letters keep their recipient block as you wrote it under **To**, instead of separate name, company and date fields. + + +## Related guides + + + + Copy a resume for an application and write its letter. + + + Track stages, what you sent and next steps. + + + Pick the design your resume and letter share. + + + Make sure software can read the resume you send with your letter. + + diff --git a/docs/images/getting-started/banner.webp b/docs/images/getting-started/banner.webp deleted file mode 100644 index 3eb7506f7..000000000 Binary files a/docs/images/getting-started/banner.webp and /dev/null differ diff --git a/docs/images/getting-started/editor-overview.webp b/docs/images/getting-started/editor-overview.webp new file mode 100644 index 000000000..deb036638 Binary files /dev/null and b/docs/images/getting-started/editor-overview.webp differ diff --git a/docs/images/getting-started/infographic.webp b/docs/images/getting-started/infographic.webp deleted file mode 100644 index 40cf10009..000000000 Binary files a/docs/images/getting-started/infographic.webp and /dev/null differ diff --git a/docs/images/getting-started/quickstart/basics-and-page.webp b/docs/images/getting-started/quickstart/basics-and-page.webp new file mode 100644 index 000000000..595903a68 Binary files /dev/null and b/docs/images/getting-started/quickstart/basics-and-page.webp differ diff --git a/docs/images/getting-started/quickstart/design-template-gallery.webp b/docs/images/getting-started/quickstart/design-template-gallery.webp new file mode 100644 index 000000000..bb3f47627 Binary files /dev/null and b/docs/images/getting-started/quickstart/design-template-gallery.webp differ diff --git a/docs/images/getting-started/quickstart/documents-empty.webp b/docs/images/getting-started/quickstart/documents-empty.webp new file mode 100644 index 000000000..40b6544cf Binary files /dev/null and b/docs/images/getting-started/quickstart/documents-empty.webp differ diff --git a/docs/images/getting-started/quickstart/editor-bar-actions.webp b/docs/images/getting-started/quickstart/editor-bar-actions.webp new file mode 100644 index 000000000..e57ce874f Binary files /dev/null and b/docs/images/getting-started/quickstart/editor-bar-actions.webp differ diff --git a/docs/images/getting-started/quickstart/share-download-tab.webp b/docs/images/getting-started/quickstart/share-download-tab.webp new file mode 100644 index 000000000..f68ff25f8 Binary files /dev/null and b/docs/images/getting-started/quickstart/share-download-tab.webp differ diff --git a/docs/images/guides/adding-an-application/add-an-application-dialog.webp b/docs/images/guides/adding-an-application/add-an-application-dialog.webp new file mode 100644 index 000000000..88e4c9bbf Binary files /dev/null and b/docs/images/guides/adding-an-application/add-an-application-dialog.webp differ diff --git a/docs/images/guides/ai-agent-tools/tool-status-in-conversation.webp b/docs/images/guides/ai-agent-tools/tool-status-in-conversation.webp new file mode 100644 index 000000000..d6f7e92d1 Binary files /dev/null and b/docs/images/guides/ai-agent-tools/tool-status-in-conversation.webp differ diff --git a/docs/images/guides/applying-custom-styles/custom-styles-editor.webp b/docs/images/guides/applying-custom-styles/custom-styles-editor.webp new file mode 100644 index 000000000..cc00e657c Binary files /dev/null and b/docs/images/guides/applying-custom-styles/custom-styles-editor.webp differ diff --git a/docs/images/guides/applying-custom-styles/find-an-entry-by-name.webp b/docs/images/guides/applying-custom-styles/find-an-entry-by-name.webp new file mode 100644 index 000000000..1c7c21500 Binary files /dev/null and b/docs/images/guides/applying-custom-styles/find-an-entry-by-name.webp differ diff --git a/docs/images/guides/applying-custom-styles/pick-an-entry-on-the-page.webp b/docs/images/guides/applying-custom-styles/pick-an-entry-on-the-page.webp new file mode 100644 index 000000000..e00bf652d Binary files /dev/null and b/docs/images/guides/applying-custom-styles/pick-an-entry-on-the-page.webp differ diff --git a/docs/images/guides/arranging-the-layout/advanced-layout-page.webp b/docs/images/guides/arranging-the-layout/advanced-layout-page.webp new file mode 100644 index 000000000..279a02fde Binary files /dev/null and b/docs/images/guides/arranging-the-layout/advanced-layout-page.webp differ diff --git a/docs/images/guides/arranging-the-layout/layout-move-to-menu.webp b/docs/images/guides/arranging-the-layout/layout-move-to-menu.webp new file mode 100644 index 000000000..33f456930 Binary files /dev/null and b/docs/images/guides/arranging-the-layout/layout-move-to-menu.webp differ diff --git a/docs/images/guides/arranging-the-layout/section-columns-menu.webp b/docs/images/guides/arranging-the-layout/section-columns-menu.webp new file mode 100644 index 000000000..63ef54af6 Binary files /dev/null and b/docs/images/guides/arranging-the-layout/section-columns-menu.webp differ diff --git a/docs/images/guides/arranging-the-layout/sidebar-panel.webp b/docs/images/guides/arranging-the-layout/sidebar-panel.webp new file mode 100644 index 000000000..ae22d279e Binary files /dev/null and b/docs/images/guides/arranging-the-layout/sidebar-panel.webp differ diff --git a/docs/images/guides/arranging-the-layout/write-outline-pages.webp b/docs/images/guides/arranging-the-layout/write-outline-pages.webp new file mode 100644 index 000000000..a49db5a8d Binary files /dev/null and b/docs/images/guides/arranging-the-layout/write-outline-pages.webp differ diff --git a/docs/images/guides/changing-appearance-and-language/account-menu-theme.webp b/docs/images/guides/changing-appearance-and-language/account-menu-theme.webp new file mode 100644 index 000000000..932139f7f Binary files /dev/null and b/docs/images/guides/changing-appearance-and-language/account-menu-theme.webp differ diff --git a/docs/images/guides/changing-appearance-and-language/preferences-page.webp b/docs/images/guides/changing-appearance-and-language/preferences-page.webp new file mode 100644 index 000000000..3a74066d2 Binary files /dev/null and b/docs/images/guides/changing-appearance-and-language/preferences-page.webp differ diff --git a/docs/images/guides/checking-your-resume/check-mode-overview.webp b/docs/images/guides/checking-your-resume/check-mode-overview.webp new file mode 100644 index 000000000..6bcaedf72 Binary files /dev/null and b/docs/images/guides/checking-your-resume/check-mode-overview.webp differ diff --git a/docs/images/guides/checking-your-resume/checks-by-category.webp b/docs/images/guides/checking-your-resume/checks-by-category.webp new file mode 100644 index 000000000..43734ceb3 Binary files /dev/null and b/docs/images/guides/checking-your-resume/checks-by-category.webp differ diff --git a/docs/images/guides/checking-your-resume/exported-pdf-pin.webp b/docs/images/guides/checking-your-resume/exported-pdf-pin.webp new file mode 100644 index 000000000..331b53fb9 Binary files /dev/null and b/docs/images/guides/checking-your-resume/exported-pdf-pin.webp differ diff --git a/docs/images/guides/checking-your-resume/exported-pdf-report.webp b/docs/images/guides/checking-your-resume/exported-pdf-report.webp new file mode 100644 index 000000000..4bb4080a4 Binary files /dev/null and b/docs/images/guides/checking-your-resume/exported-pdf-report.webp differ diff --git a/docs/images/guides/checking-your-resume/issue-pinned-on-page.webp b/docs/images/guides/checking-your-resume/issue-pinned-on-page.webp new file mode 100644 index 000000000..d33dbb8e9 Binary files /dev/null and b/docs/images/guides/checking-your-resume/issue-pinned-on-page.webp differ diff --git a/docs/images/guides/checking-your-resume/job-match-linked-application.webp b/docs/images/guides/checking-your-resume/job-match-linked-application.webp new file mode 100644 index 000000000..f44048ed7 Binary files /dev/null and b/docs/images/guides/checking-your-resume/job-match-linked-application.webp differ diff --git a/docs/images/guides/checking-your-resume/job-match-pasted-posting.webp b/docs/images/guides/checking-your-resume/job-match-pasted-posting.webp new file mode 100644 index 000000000..b92df1089 Binary files /dev/null and b/docs/images/guides/checking-your-resume/job-match-pasted-posting.webp differ diff --git a/docs/images/guides/checking-your-resume/parser-view.webp b/docs/images/guides/checking-your-resume/parser-view.webp new file mode 100644 index 000000000..6859ec50d Binary files /dev/null and b/docs/images/guides/checking-your-resume/parser-view.webp differ diff --git a/docs/images/guides/checking-your-resume/score-and-issues.webp b/docs/images/guides/checking-your-resume/score-and-issues.webp new file mode 100644 index 000000000..9dd58f72b Binary files /dev/null and b/docs/images/guides/checking-your-resume/score-and-issues.webp differ diff --git a/docs/images/guides/checking-your-resume/writing-review-start.webp b/docs/images/guides/checking-your-resume/writing-review-start.webp new file mode 100644 index 000000000..bd33bd79d Binary files /dev/null and b/docs/images/guides/checking-your-resume/writing-review-start.webp differ diff --git a/docs/images/guides/choosing-a-template/screenshot-1.webp b/docs/images/guides/choosing-a-template/screenshot-1.webp deleted file mode 100644 index 061fabebe..000000000 Binary files a/docs/images/guides/choosing-a-template/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/choosing-a-template/screenshot-2.webp b/docs/images/guides/choosing-a-template/screenshot-2.webp deleted file mode 100644 index a8cddedde..000000000 Binary files a/docs/images/guides/choosing-a-template/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/choosing-a-template/screenshot-3.webp b/docs/images/guides/choosing-a-template/screenshot-3.webp deleted file mode 100644 index 289ebf08a..000000000 Binary files a/docs/images/guides/choosing-a-template/screenshot-3.webp and /dev/null differ diff --git a/docs/images/guides/choosing-a-template/screenshot-4.webp b/docs/images/guides/choosing-a-template/screenshot-4.webp deleted file mode 100644 index 0c50b428c..000000000 Binary files a/docs/images/guides/choosing-a-template/screenshot-4.webp and /dev/null differ diff --git a/docs/images/guides/choosing-a-template/template-gallery.webp b/docs/images/guides/choosing-a-template/template-gallery.webp new file mode 100644 index 000000000..e3aa61f91 Binary files /dev/null and b/docs/images/guides/choosing-a-template/template-gallery.webp differ diff --git a/docs/images/guides/choosing-a-template/template-hover-preview.webp b/docs/images/guides/choosing-a-template/template-hover-preview.webp new file mode 100644 index 000000000..288f915ca Binary files /dev/null and b/docs/images/guides/choosing-a-template/template-hover-preview.webp differ diff --git a/docs/images/guides/choosing-colors/advanced-colors-and-level.webp b/docs/images/guides/choosing-colors/advanced-colors-and-level.webp new file mode 100644 index 000000000..136d2e032 Binary files /dev/null and b/docs/images/guides/choosing-colors/advanced-colors-and-level.webp differ diff --git a/docs/images/guides/choosing-colors/color-group.webp b/docs/images/guides/choosing-colors/color-group.webp new file mode 100644 index 000000000..f2e5f813e Binary files /dev/null and b/docs/images/guides/choosing-colors/color-group.webp differ diff --git a/docs/images/guides/choosing-colors/contrast-warning.webp b/docs/images/guides/choosing-colors/contrast-warning.webp new file mode 100644 index 000000000..20a0f8614 Binary files /dev/null and b/docs/images/guides/choosing-colors/contrast-warning.webp differ diff --git a/docs/images/guides/creating-an-account/check-your-email.webp b/docs/images/guides/creating-an-account/check-your-email.webp new file mode 100644 index 000000000..032438ef2 Binary files /dev/null and b/docs/images/guides/creating-an-account/check-your-email.webp differ diff --git a/docs/images/guides/creating-an-account/sign-up-form.webp b/docs/images/guides/creating-an-account/sign-up-form.webp new file mode 100644 index 000000000..e08cf44d6 Binary files /dev/null and b/docs/images/guides/creating-an-account/sign-up-form.webp differ diff --git a/docs/images/guides/creating-your-first-resume/documents-first-visit.webp b/docs/images/guides/creating-your-first-resume/documents-first-visit.webp new file mode 100644 index 000000000..277c0ff08 Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/documents-first-visit.webp differ diff --git a/docs/images/guides/creating-your-first-resume/download-pdf-button.webp b/docs/images/guides/creating-your-first-resume/download-pdf-button.webp new file mode 100644 index 000000000..2921c8e55 Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/download-pdf-button.webp differ diff --git a/docs/images/guides/creating-your-first-resume/editor-with-basics.webp b/docs/images/guides/creating-your-first-resume/editor-with-basics.webp new file mode 100644 index 000000000..cc3b3faa5 Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/editor-with-basics.webp differ diff --git a/docs/images/guides/creating-your-first-resume/first-entry.webp b/docs/images/guides/creating-your-first-resume/first-entry.webp new file mode 100644 index 000000000..0b0d5df7b Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/first-entry.webp differ diff --git a/docs/images/guides/creating-your-first-resume/new-document-dialog.webp b/docs/images/guides/creating-your-first-resume/new-document-dialog.webp new file mode 100644 index 000000000..aeff319a6 Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/new-document-dialog.webp differ diff --git a/docs/images/guides/creating-your-first-resume/screenshot-1.webp b/docs/images/guides/creating-your-first-resume/screenshot-1.webp deleted file mode 100644 index b3f70966b..000000000 Binary files a/docs/images/guides/creating-your-first-resume/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/creating-your-first-resume/starter-sections.webp b/docs/images/guides/creating-your-first-resume/starter-sections.webp new file mode 100644 index 000000000..4a39e0e6c Binary files /dev/null and b/docs/images/guides/creating-your-first-resume/starter-sections.webp differ diff --git a/docs/images/guides/customizing-typography/advanced-typography.webp b/docs/images/guides/customizing-typography/advanced-typography.webp new file mode 100644 index 000000000..e5ef6d8cc Binary files /dev/null and b/docs/images/guides/customizing-typography/advanced-typography.webp differ diff --git a/docs/images/guides/customizing-typography/font-family-search.webp b/docs/images/guides/customizing-typography/font-family-search.webp new file mode 100644 index 000000000..dfc25ea11 Binary files /dev/null and b/docs/images/guides/customizing-typography/font-family-search.webp differ diff --git a/docs/images/guides/customizing-typography/type-group.webp b/docs/images/guides/customizing-typography/type-group.webp new file mode 100644 index 000000000..dea31393e Binary files /dev/null and b/docs/images/guides/customizing-typography/type-group.webp differ diff --git a/docs/images/guides/deleting-your-account/delete-account-dialog.webp b/docs/images/guides/deleting-your-account/delete-account-dialog.webp new file mode 100644 index 000000000..d8f924d1c Binary files /dev/null and b/docs/images/guides/deleting-your-account/delete-account-dialog.webp differ diff --git a/docs/images/guides/editing-entries/entry-move-to-menu.webp b/docs/images/guides/editing-entries/entry-move-to-menu.webp new file mode 100644 index 000000000..d61b56ab1 Binary files /dev/null and b/docs/images/guides/editing-entries/entry-move-to-menu.webp differ diff --git a/docs/images/guides/editing-entries/experience-roles.webp b/docs/images/guides/editing-entries/experience-roles.webp new file mode 100644 index 000000000..f436deeb5 Binary files /dev/null and b/docs/images/guides/editing-entries/experience-roles.webp differ diff --git a/docs/images/guides/editing-entries/new-draft-entry.webp b/docs/images/guides/editing-entries/new-draft-entry.webp new file mode 100644 index 000000000..8770d922f Binary files /dev/null and b/docs/images/guides/editing-entries/new-draft-entry.webp differ diff --git a/docs/images/guides/editing-entries/roles-on-page.webp b/docs/images/guides/editing-entries/roles-on-page.webp new file mode 100644 index 000000000..a509ba92a Binary files /dev/null and b/docs/images/guides/editing-entries/roles-on-page.webp differ diff --git a/docs/images/guides/editing-entries/skill-more-options.webp b/docs/images/guides/editing-entries/skill-more-options.webp new file mode 100644 index 000000000..e644b11ce Binary files /dev/null and b/docs/images/guides/editing-entries/skill-more-options.webp differ diff --git a/docs/images/guides/editing-on-mobile/phone-design-sheet.webp b/docs/images/guides/editing-on-mobile/phone-design-sheet.webp new file mode 100644 index 000000000..a552e41eb Binary files /dev/null and b/docs/images/guides/editing-on-mobile/phone-design-sheet.webp differ diff --git a/docs/images/guides/editing-on-mobile/phone-entry-screen.webp b/docs/images/guides/editing-on-mobile/phone-entry-screen.webp new file mode 100644 index 000000000..e207f2db6 Binary files /dev/null and b/docs/images/guides/editing-on-mobile/phone-entry-screen.webp differ diff --git a/docs/images/guides/editing-on-mobile/phone-page-edit-entry.webp b/docs/images/guides/editing-on-mobile/phone-page-edit-entry.webp new file mode 100644 index 000000000..73e795d7e Binary files /dev/null and b/docs/images/guides/editing-on-mobile/phone-page-edit-entry.webp differ diff --git a/docs/images/guides/editing-on-mobile/phone-write-view.webp b/docs/images/guides/editing-on-mobile/phone-write-view.webp new file mode 100644 index 000000000..824b26332 Binary files /dev/null and b/docs/images/guides/editing-on-mobile/phone-write-view.webp differ diff --git a/docs/images/guides/editing-on-mobile/tablet-panel-drawer.webp b/docs/images/guides/editing-on-mobile/tablet-panel-drawer.webp new file mode 100644 index 000000000..3afe86040 Binary files /dev/null and b/docs/images/guides/editing-on-mobile/tablet-panel-drawer.webp differ diff --git a/docs/images/guides/editor-overview/document-menu.webp b/docs/images/guides/editor-overview/document-menu.webp new file mode 100644 index 000000000..65cc7cac3 Binary files /dev/null and b/docs/images/guides/editor-overview/document-menu.webp differ diff --git a/docs/images/guides/editor-overview/editor-bar.webp b/docs/images/guides/editor-overview/editor-bar.webp new file mode 100644 index 000000000..f5e3fa883 Binary files /dev/null and b/docs/images/guides/editor-overview/editor-bar.webp differ diff --git a/docs/images/guides/editor-overview/editor-write-mode.webp b/docs/images/guides/editor-overview/editor-write-mode.webp new file mode 100644 index 000000000..deb036638 Binary files /dev/null and b/docs/images/guides/editor-overview/editor-write-mode.webp differ diff --git a/docs/images/guides/editor-overview/locked-document.webp b/docs/images/guides/editor-overview/locked-document.webp new file mode 100644 index 000000000..3e35adf1b Binary files /dev/null and b/docs/images/guides/editor-overview/locked-document.webp differ diff --git a/docs/images/guides/editor-overview/offline-save-status.webp b/docs/images/guides/editor-overview/offline-save-status.webp new file mode 100644 index 000000000..5fbcd07a3 Binary files /dev/null and b/docs/images/guides/editor-overview/offline-save-status.webp differ diff --git a/docs/images/guides/editor-overview/zoom-bar.webp b/docs/images/guides/editor-overview/zoom-bar.webp new file mode 100644 index 000000000..b292e5093 Binary files /dev/null and b/docs/images/guides/editor-overview/zoom-bar.webp differ diff --git a/docs/images/guides/entering-dates/dates-on-page.webp b/docs/images/guides/entering-dates/dates-on-page.webp new file mode 100644 index 000000000..423924a7f Binary files /dev/null and b/docs/images/guides/entering-dates/dates-on-page.webp differ diff --git a/docs/images/guides/entering-dates/dates-present.webp b/docs/images/guides/entering-dates/dates-present.webp new file mode 100644 index 000000000..b2a731337 Binary files /dev/null and b/docs/images/guides/entering-dates/dates-present.webp differ diff --git a/docs/images/guides/entering-dates/dates-unreadable-error.webp b/docs/images/guides/entering-dates/dates-unreadable-error.webp new file mode 100644 index 000000000..bfc73022f Binary files /dev/null and b/docs/images/guides/entering-dates/dates-unreadable-error.webp differ diff --git a/docs/images/guides/entering-dates/design-advanced-date-format.webp b/docs/images/guides/entering-dates/design-advanced-date-format.webp new file mode 100644 index 000000000..f71382c58 Binary files /dev/null and b/docs/images/guides/entering-dates/design-advanced-date-format.webp differ diff --git a/docs/images/guides/exporting-resume-to-markdown/download-tab-markdown.webp b/docs/images/guides/exporting-resume-to-markdown/download-tab-markdown.webp new file mode 100644 index 000000000..4fbe812f9 Binary files /dev/null and b/docs/images/guides/exporting-resume-to-markdown/download-tab-markdown.webp differ diff --git a/docs/images/guides/exporting-resume-to-markdown/screenshot-1.webp b/docs/images/guides/exporting-resume-to-markdown/screenshot-1.webp deleted file mode 100644 index b6e2da598..000000000 Binary files a/docs/images/guides/exporting-resume-to-markdown/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/exporting-your-data/your-data-section.webp b/docs/images/guides/exporting-your-data/your-data-section.webp new file mode 100644 index 000000000..540d8583f Binary files /dev/null and b/docs/images/guides/exporting-your-data/your-data-section.webp differ diff --git a/docs/images/guides/exporting-your-resume/document-menu-print.webp b/docs/images/guides/exporting-your-resume/document-menu-print.webp new file mode 100644 index 000000000..d49ff2079 Binary files /dev/null and b/docs/images/guides/exporting-your-resume/document-menu-print.webp differ diff --git a/docs/images/guides/exporting-your-resume/download-pdf-button.webp b/docs/images/guides/exporting-your-resume/download-pdf-button.webp new file mode 100644 index 000000000..6fd0f2f2b Binary files /dev/null and b/docs/images/guides/exporting-your-resume/download-pdf-button.webp differ diff --git a/docs/images/guides/exporting-your-resume/screenshot-1.webp b/docs/images/guides/exporting-your-resume/screenshot-1.webp deleted file mode 100644 index a5f2a62d8..000000000 Binary files a/docs/images/guides/exporting-your-resume/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/exporting-your-resume/share-sheet-download-tab.webp b/docs/images/guides/exporting-your-resume/share-sheet-download-tab.webp new file mode 100644 index 000000000..16acaea17 Binary files /dev/null and b/docs/images/guides/exporting-your-resume/share-sheet-download-tab.webp differ diff --git a/docs/images/guides/filling-in-your-details/basics-card.webp b/docs/images/guides/filling-in-your-details/basics-card.webp new file mode 100644 index 000000000..a7b772239 Binary files /dev/null and b/docs/images/guides/filling-in-your-details/basics-card.webp differ diff --git a/docs/images/guides/filling-in-your-details/crop-picture-dialog.webp b/docs/images/guides/filling-in-your-details/crop-picture-dialog.webp new file mode 100644 index 000000000..d0873d869 Binary files /dev/null and b/docs/images/guides/filling-in-your-details/crop-picture-dialog.webp differ diff --git a/docs/images/guides/filling-in-your-details/photo-popover.webp b/docs/images/guides/filling-in-your-details/photo-popover.webp new file mode 100644 index 000000000..643eac888 Binary files /dev/null and b/docs/images/guides/filling-in-your-details/photo-popover.webp differ diff --git a/docs/images/guides/filling-in-your-details/summary-editor.webp b/docs/images/guides/filling-in-your-details/summary-editor.webp new file mode 100644 index 000000000..a4afabaf7 Binary files /dev/null and b/docs/images/guides/filling-in-your-details/summary-editor.webp differ diff --git a/docs/images/guides/fitting-content-on-a-page/overflow-chip.webp b/docs/images/guides/fitting-content-on-a-page/overflow-chip.webp new file mode 100644 index 000000000..09915446b Binary files /dev/null and b/docs/images/guides/fitting-content-on-a-page/overflow-chip.webp differ diff --git a/docs/images/guides/fitting-content-on-a-page/page-boundary-marker.webp b/docs/images/guides/fitting-content-on-a-page/page-boundary-marker.webp new file mode 100644 index 000000000..01a89d766 Binary files /dev/null and b/docs/images/guides/fitting-content-on-a-page/page-boundary-marker.webp differ diff --git a/docs/images/guides/fitting-content-on-a-page/screenshot-1.webp b/docs/images/guides/fitting-content-on-a-page/screenshot-1.webp deleted file mode 100644 index f6764cf8b..000000000 Binary files a/docs/images/guides/fitting-content-on-a-page/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/fitting-content-on-a-page/screenshot-2.webp b/docs/images/guides/fitting-content-on-a-page/screenshot-2.webp deleted file mode 100644 index 4e1af8404..000000000 Binary files a/docs/images/guides/fitting-content-on-a-page/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/fitting-content-on-a-page/zoom-bar-page-count.webp b/docs/images/guides/fitting-content-on-a-page/zoom-bar-page-count.webp new file mode 100644 index 000000000..6c36cae4d Binary files /dev/null and b/docs/images/guides/fitting-content-on-a-page/zoom-bar-page-count.webp differ diff --git a/docs/images/guides/formatting-text/formatting-toolbar.webp b/docs/images/guides/formatting-text/formatting-toolbar.webp new file mode 100644 index 000000000..0aa7878ac Binary files /dev/null and b/docs/images/guides/formatting-text/formatting-toolbar.webp differ diff --git a/docs/images/guides/formatting-text/link-address-dialog.webp b/docs/images/guides/formatting-text/link-address-dialog.webp new file mode 100644 index 000000000..7da0387a4 Binary files /dev/null and b/docs/images/guides/formatting-text/link-address-dialog.webp differ diff --git a/docs/images/guides/formatting-text/phone-formatting-toolbar.webp b/docs/images/guides/formatting-text/phone-formatting-toolbar.webp new file mode 100644 index 000000000..976d3fe05 Binary files /dev/null and b/docs/images/guides/formatting-text/phone-formatting-toolbar.webp differ diff --git a/docs/images/guides/importing-applications-from-csv/export-applications-sheet.webp b/docs/images/guides/importing-applications-from-csv/export-applications-sheet.webp new file mode 100644 index 000000000..1a545d2fb Binary files /dev/null and b/docs/images/guides/importing-applications-from-csv/export-applications-sheet.webp differ diff --git a/docs/images/guides/importing-applications-from-csv/import-from-csv-column-matching.webp b/docs/images/guides/importing-applications-from-csv/import-from-csv-column-matching.webp new file mode 100644 index 000000000..a03e870ad Binary files /dev/null and b/docs/images/guides/importing-applications-from-csv/import-from-csv-column-matching.webp differ diff --git a/docs/images/guides/importing-applications-from-csv/import-from-csv-ready-summary.webp b/docs/images/guides/importing-applications-from-csv/import-from-csv-ready-summary.webp new file mode 100644 index 000000000..e83d36bbd Binary files /dev/null and b/docs/images/guides/importing-applications-from-csv/import-from-csv-ready-summary.webp differ diff --git a/docs/images/guides/importing-applications-from-csv/screenshot-1.webp b/docs/images/guides/importing-applications-from-csv/screenshot-1.webp deleted file mode 100644 index 66187fd42..000000000 Binary files a/docs/images/guides/importing-applications-from-csv/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/importing-resumes/drop-to-import.webp b/docs/images/guides/importing-resumes/drop-to-import.webp new file mode 100644 index 000000000..9ad897686 Binary files /dev/null and b/docs/images/guides/importing-resumes/drop-to-import.webp differ diff --git a/docs/images/guides/importing-resumes/import-finished.webp b/docs/images/guides/importing-resumes/import-finished.webp new file mode 100644 index 000000000..549fa29d9 Binary files /dev/null and b/docs/images/guides/importing-resumes/import-finished.webp differ diff --git a/docs/images/guides/importing-resumes/import-word-needs-ai.webp b/docs/images/guides/importing-resumes/import-word-needs-ai.webp new file mode 100644 index 000000000..41a45f64d Binary files /dev/null and b/docs/images/guides/importing-resumes/import-word-needs-ai.webp differ diff --git a/docs/images/guides/importing-resumes/imported-from-note.webp b/docs/images/guides/importing-resumes/imported-from-note.webp new file mode 100644 index 000000000..dcac13afc Binary files /dev/null and b/docs/images/guides/importing-resumes/imported-from-note.webp differ diff --git a/docs/images/guides/importing-resumes/screenshot-1.webp b/docs/images/guides/importing-resumes/screenshot-1.webp deleted file mode 100644 index d0d07119e..000000000 Binary files a/docs/images/guides/importing-resumes/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/linking-social-accounts/sign-in-and-security-providers.webp b/docs/images/guides/linking-social-accounts/sign-in-and-security-providers.webp new file mode 100644 index 000000000..8b1fd1cbb Binary files /dev/null and b/docs/images/guides/linking-social-accounts/sign-in-and-security-providers.webp differ diff --git a/docs/images/guides/managing-an-application/application-detail-sheet.webp b/docs/images/guides/managing-an-application/application-detail-sheet.webp new file mode 100644 index 000000000..d9a57d6ad Binary files /dev/null and b/docs/images/guides/managing-an-application/application-detail-sheet.webp differ diff --git a/docs/images/guides/managing-an-application/close-this-application-dialog.webp b/docs/images/guides/managing-an-application/close-this-application-dialog.webp new file mode 100644 index 000000000..63929cbc4 Binary files /dev/null and b/docs/images/guides/managing-an-application/close-this-application-dialog.webp differ diff --git a/docs/images/guides/managing-an-application/detail-sheet-contacts.webp b/docs/images/guides/managing-an-application/detail-sheet-contacts.webp new file mode 100644 index 000000000..571636879 Binary files /dev/null and b/docs/images/guides/managing-an-application/detail-sheet-contacts.webp differ diff --git a/docs/images/guides/managing-an-application/detail-sheet-next-step.webp b/docs/images/guides/managing-an-application/detail-sheet-next-step.webp new file mode 100644 index 000000000..8f875046c Binary files /dev/null and b/docs/images/guides/managing-an-application/detail-sheet-next-step.webp differ diff --git a/docs/images/guides/managing-an-application/detail-sheet-notes-and-activity.webp b/docs/images/guides/managing-an-application/detail-sheet-notes-and-activity.webp new file mode 100644 index 000000000..5c05716fb Binary files /dev/null and b/docs/images/guides/managing-an-application/detail-sheet-notes-and-activity.webp differ diff --git a/docs/images/guides/managing-an-application/detail-sheet-stage-stepper.webp b/docs/images/guides/managing-an-application/detail-sheet-stage-stepper.webp new file mode 100644 index 000000000..79dd17733 Binary files /dev/null and b/docs/images/guides/managing-an-application/detail-sheet-stage-stepper.webp differ diff --git a/docs/images/guides/managing-an-application/detail-sheet-what-you-sent.webp b/docs/images/guides/managing-an-application/detail-sheet-what-you-sent.webp new file mode 100644 index 000000000..3c676ffc8 Binary files /dev/null and b/docs/images/guides/managing-an-application/detail-sheet-what-you-sent.webp differ diff --git a/docs/images/guides/managing-an-application/edit-application-sheet.webp b/docs/images/guides/managing-an-application/edit-application-sheet.webp new file mode 100644 index 000000000..846475c9d Binary files /dev/null and b/docs/images/guides/managing-an-application/edit-application-sheet.webp differ diff --git a/docs/images/guides/managing-an-application/what-you-sent-attach-files.webp b/docs/images/guides/managing-an-application/what-you-sent-attach-files.webp new file mode 100644 index 000000000..5928fba47 Binary files /dev/null and b/docs/images/guides/managing-an-application/what-you-sent-attach-files.webp differ diff --git a/docs/images/guides/managing-documents/copy-for-a-job.webp b/docs/images/guides/managing-documents/copy-for-a-job.webp new file mode 100644 index 000000000..2fbd36207 Binary files /dev/null and b/docs/images/guides/managing-documents/copy-for-a-job.webp differ diff --git a/docs/images/guides/managing-documents/document-options-menu.webp b/docs/images/guides/managing-documents/document-options-menu.webp new file mode 100644 index 000000000..956e52e94 Binary files /dev/null and b/docs/images/guides/managing-documents/document-options-menu.webp differ diff --git a/docs/images/guides/managing-documents/documents-grid.webp b/docs/images/guides/managing-documents/documents-grid.webp new file mode 100644 index 000000000..71bd4a9e9 Binary files /dev/null and b/docs/images/guides/managing-documents/documents-grid.webp differ diff --git a/docs/images/guides/managing-documents/documents-list-view.webp b/docs/images/guides/managing-documents/documents-list-view.webp new file mode 100644 index 000000000..3e7f26de3 Binary files /dev/null and b/docs/images/guides/managing-documents/documents-list-view.webp differ diff --git a/docs/images/guides/managing-documents/documents-toolbar.webp b/docs/images/guides/managing-documents/documents-toolbar.webp new file mode 100644 index 000000000..e8fcb326e Binary files /dev/null and b/docs/images/guides/managing-documents/documents-toolbar.webp differ diff --git a/docs/images/guides/managing-documents/locked-document-menu.webp b/docs/images/guides/managing-documents/locked-document-menu.webp new file mode 100644 index 000000000..bde029364 Binary files /dev/null and b/docs/images/guides/managing-documents/locked-document-menu.webp differ diff --git a/docs/images/guides/managing-documents/new-document-dialog.webp b/docs/images/guides/managing-documents/new-document-dialog.webp new file mode 100644 index 000000000..aeff319a6 Binary files /dev/null and b/docs/images/guides/managing-documents/new-document-dialog.webp differ diff --git a/docs/images/guides/managing-documents/rename-inline.webp b/docs/images/guides/managing-documents/rename-inline.webp new file mode 100644 index 000000000..8fbe3faae Binary files /dev/null and b/docs/images/guides/managing-documents/rename-inline.webp differ diff --git a/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-1.webp b/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-1.webp deleted file mode 100644 index 4794cbe8e..000000000 Binary files a/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-2.webp b/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-2.webp deleted file mode 100644 index 4edb11708..000000000 Binary files a/docs/images/guides/managing-resumes-from-the-dashboard/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/managing-sections/add-section-menu.webp b/docs/images/guides/managing-sections/add-section-menu.webp new file mode 100644 index 000000000..38aaaafc2 Binary files /dev/null and b/docs/images/guides/managing-sections/add-section-menu.webp differ diff --git a/docs/images/guides/managing-sections/outline-print-order.webp b/docs/images/guides/managing-sections/outline-print-order.webp new file mode 100644 index 000000000..64cba308b Binary files /dev/null and b/docs/images/guides/managing-sections/outline-print-order.webp differ diff --git a/docs/images/guides/managing-sections/rename-section-dialog.webp b/docs/images/guides/managing-sections/rename-section-dialog.webp new file mode 100644 index 000000000..4f9d05be6 Binary files /dev/null and b/docs/images/guides/managing-sections/rename-section-dialog.webp differ diff --git a/docs/images/guides/managing-sections/section-options-menu.webp b/docs/images/guides/managing-sections/section-options-menu.webp new file mode 100644 index 000000000..c4f0d7f27 Binary files /dev/null and b/docs/images/guides/managing-sections/section-options-menu.webp differ diff --git a/docs/images/guides/managing-sections/start-suggestions.webp b/docs/images/guides/managing-sections/start-suggestions.webp new file mode 100644 index 000000000..7b6e53e47 Binary files /dev/null and b/docs/images/guides/managing-sections/start-suggestions.webp differ diff --git a/docs/images/guides/moving-items-between-sections/screenshot-1.webp b/docs/images/guides/moving-items-between-sections/screenshot-1.webp deleted file mode 100644 index a244631aa..000000000 Binary files a/docs/images/guides/moving-items-between-sections/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/moving-items-between-sections/screenshot-2.webp b/docs/images/guides/moving-items-between-sections/screenshot-2.webp deleted file mode 100644 index 89a7f15d8..000000000 Binary files a/docs/images/guides/moving-items-between-sections/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/moving-items-between-sections/screenshot-3.webp b/docs/images/guides/moving-items-between-sections/screenshot-3.webp deleted file mode 100644 index 91551d729..000000000 Binary files a/docs/images/guides/moving-items-between-sections/screenshot-3.webp and /dev/null differ diff --git a/docs/images/guides/moving-items-between-sections/screenshot-4.webp b/docs/images/guides/moving-items-between-sections/screenshot-4.webp deleted file mode 100644 index 8b33fb9fd..000000000 Binary files a/docs/images/guides/moving-items-between-sections/screenshot-4.webp and /dev/null differ diff --git a/docs/images/guides/organizing-with-tags/tag-filter-active.webp b/docs/images/guides/organizing-with-tags/tag-filter-active.webp new file mode 100644 index 000000000..f43366551 Binary files /dev/null and b/docs/images/guides/organizing-with-tags/tag-filter-active.webp differ diff --git a/docs/images/guides/organizing-with-tags/tags-dialog.webp b/docs/images/guides/organizing-with-tags/tags-dialog.webp new file mode 100644 index 000000000..78c31efdf Binary files /dev/null and b/docs/images/guides/organizing-with-tags/tags-dialog.webp differ diff --git a/docs/images/guides/scheduling-interviews/applications-calendar-view.webp b/docs/images/guides/scheduling-interviews/applications-calendar-view.webp new file mode 100644 index 000000000..5e65574ba Binary files /dev/null and b/docs/images/guides/scheduling-interviews/applications-calendar-view.webp differ diff --git a/docs/images/guides/scheduling-interviews/follow-up-dialog.webp b/docs/images/guides/scheduling-interviews/follow-up-dialog.webp new file mode 100644 index 000000000..29f0bf20e Binary files /dev/null and b/docs/images/guides/scheduling-interviews/follow-up-dialog.webp differ diff --git a/docs/images/guides/scheduling-interviews/next-step-edit-menu.webp b/docs/images/guides/scheduling-interviews/next-step-edit-menu.webp new file mode 100644 index 000000000..0f44370cc Binary files /dev/null and b/docs/images/guides/scheduling-interviews/next-step-edit-menu.webp differ diff --git a/docs/images/guides/scheduling-interviews/schedule-an-interview-dialog.webp b/docs/images/guides/scheduling-interviews/schedule-an-interview-dialog.webp new file mode 100644 index 000000000..a9eb9c919 Binary files /dev/null and b/docs/images/guides/scheduling-interviews/schedule-an-interview-dialog.webp differ diff --git a/docs/images/guides/selecting-page-format/advanced-page.webp b/docs/images/guides/selecting-page-format/advanced-page.webp new file mode 100644 index 000000000..66374099e Binary files /dev/null and b/docs/images/guides/selecting-page-format/advanced-page.webp differ diff --git a/docs/images/guides/selecting-page-format/page-group.webp b/docs/images/guides/selecting-page-format/page-group.webp new file mode 100644 index 000000000..355dfa7af Binary files /dev/null and b/docs/images/guides/selecting-page-format/page-group.webp differ diff --git a/docs/images/guides/selecting-page-format/screenshot-1.webp b/docs/images/guides/selecting-page-format/screenshot-1.webp deleted file mode 100644 index c73f9c988..000000000 Binary files a/docs/images/guides/selecting-page-format/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/setting-up-passkeys/name-this-passkey.webp b/docs/images/guides/setting-up-passkeys/name-this-passkey.webp new file mode 100644 index 000000000..0c29ab106 Binary files /dev/null and b/docs/images/guides/setting-up-passkeys/name-this-passkey.webp differ diff --git a/docs/images/guides/setting-up-passkeys/passkeys-list.webp b/docs/images/guides/setting-up-passkeys/passkeys-list.webp new file mode 100644 index 000000000..8d608ecb6 Binary files /dev/null and b/docs/images/guides/setting-up-passkeys/passkeys-list.webp differ diff --git a/docs/images/guides/setting-up-two-factor-authentication/backup-codes.webp b/docs/images/guides/setting-up-two-factor-authentication/backup-codes.webp new file mode 100644 index 000000000..f4fd7b821 Binary files /dev/null and b/docs/images/guides/setting-up-two-factor-authentication/backup-codes.webp differ diff --git a/docs/images/guides/setting-up-two-factor-authentication/disable-dialog.webp b/docs/images/guides/setting-up-two-factor-authentication/disable-dialog.webp new file mode 100644 index 000000000..b1921a118 Binary files /dev/null and b/docs/images/guides/setting-up-two-factor-authentication/disable-dialog.webp differ diff --git a/docs/images/guides/setting-up-two-factor-authentication/enter-password.webp b/docs/images/guides/setting-up-two-factor-authentication/enter-password.webp new file mode 100644 index 000000000..f12126602 Binary files /dev/null and b/docs/images/guides/setting-up-two-factor-authentication/enter-password.webp differ diff --git a/docs/images/guides/setting-up-two-factor-authentication/scan-qr-code.webp b/docs/images/guides/setting-up-two-factor-authentication/scan-qr-code.webp new file mode 100644 index 000000000..8b35dd220 Binary files /dev/null and b/docs/images/guides/setting-up-two-factor-authentication/scan-qr-code.webp differ diff --git a/docs/images/guides/setting-up-two-factor-authentication/two-step-verification-on.webp b/docs/images/guides/setting-up-two-factor-authentication/two-step-verification-on.webp new file mode 100644 index 000000000..0f21c0acd Binary files /dev/null and b/docs/images/guides/setting-up-two-factor-authentication/two-step-verification-on.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/address-invalid.webp b/docs/images/guides/sharing-your-resume-publicly/address-invalid.webp new file mode 100644 index 000000000..1a46ed703 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/address-invalid.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/editor-bar-share-button.webp b/docs/images/guides/sharing-your-resume-publicly/editor-bar-share-button.webp new file mode 100644 index 000000000..183f3f67d Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/editor-bar-share-button.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/password-dialog.webp b/docs/images/guides/sharing-your-resume-publicly/password-dialog.webp new file mode 100644 index 000000000..880dbb811 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/password-dialog.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/password-gate.webp b/docs/images/guides/sharing-your-resume-publicly/password-gate.webp new file mode 100644 index 000000000..2ed148b43 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/password-gate.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/public-link-off.webp b/docs/images/guides/sharing-your-resume-publicly/public-link-off.webp new file mode 100644 index 000000000..ef88f49a1 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/public-link-off.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/public-resume-on-phone.webp b/docs/images/guides/sharing-your-resume-publicly/public-resume-on-phone.webp new file mode 100644 index 000000000..82b32e936 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/public-resume-on-phone.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/public-resume-page.webp b/docs/images/guides/sharing-your-resume-publicly/public-resume-page.webp new file mode 100644 index 000000000..024871e43 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/public-resume-page.webp differ diff --git a/docs/images/guides/sharing-your-resume-publicly/screenshot-1.webp b/docs/images/guides/sharing-your-resume-publicly/screenshot-1.webp deleted file mode 100644 index eb29eaab0..000000000 Binary files a/docs/images/guides/sharing-your-resume-publicly/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/sharing-your-resume-publicly/share-sheet-link-tab.webp b/docs/images/guides/sharing-your-resume-publicly/share-sheet-link-tab.webp new file mode 100644 index 000000000..361eae1d9 Binary files /dev/null and b/docs/images/guides/sharing-your-resume-publicly/share-sheet-link-tab.webp differ diff --git a/docs/images/guides/signing-in/backup-code.webp b/docs/images/guides/signing-in/backup-code.webp new file mode 100644 index 000000000..558db2b1e Binary files /dev/null and b/docs/images/guides/signing-in/backup-code.webp differ diff --git a/docs/images/guides/signing-in/forgot-password.webp b/docs/images/guides/signing-in/forgot-password.webp new file mode 100644 index 000000000..8bffcc12d Binary files /dev/null and b/docs/images/guides/signing-in/forgot-password.webp differ diff --git a/docs/images/guides/signing-in/sign-in-page.webp b/docs/images/guides/signing-in/sign-in-page.webp new file mode 100644 index 000000000..317511c9b Binary files /dev/null and b/docs/images/guides/signing-in/sign-in-page.webp differ diff --git a/docs/images/guides/signing-in/two-factor-code.webp b/docs/images/guides/signing-in/two-factor-code.webp new file mode 100644 index 000000000..ff1626859 Binary files /dev/null and b/docs/images/guides/signing-in/two-factor-code.webp differ diff --git a/docs/images/guides/tailoring-a-resume-for-a-job/copy-a-resume-for-a-job-dialog.webp b/docs/images/guides/tailoring-a-resume-for-a-job/copy-a-resume-for-a-job-dialog.webp new file mode 100644 index 000000000..ebf368204 Binary files /dev/null and b/docs/images/guides/tailoring-a-resume-for-a-job/copy-a-resume-for-a-job-dialog.webp differ diff --git a/docs/images/guides/tailoring-a-resume-for-a-job/prepare-for-next-step-suggestions.webp b/docs/images/guides/tailoring-a-resume-for-a-job/prepare-for-next-step-suggestions.webp new file mode 100644 index 000000000..72f0ccbf4 Binary files /dev/null and b/docs/images/guides/tailoring-a-resume-for-a-job/prepare-for-next-step-suggestions.webp differ diff --git a/docs/images/guides/tailoring-a-resume-for-a-job/what-you-sent-tailor-and-write-buttons.webp b/docs/images/guides/tailoring-a-resume-for-a-job/what-you-sent-tailor-and-write-buttons.webp new file mode 100644 index 000000000..c5ab3022b Binary files /dev/null and b/docs/images/guides/tailoring-a-resume-for-a-job/what-you-sent-tailor-and-write-buttons.webp differ diff --git a/docs/images/guides/tracking-job-applications/applications-board-view.webp b/docs/images/guides/tracking-job-applications/applications-board-view.webp new file mode 100644 index 000000000..6e2ecd6fe Binary files /dev/null and b/docs/images/guides/tracking-job-applications/applications-board-view.webp differ diff --git a/docs/images/guides/tracking-job-applications/applications-list-view.webp b/docs/images/guides/tracking-job-applications/applications-list-view.webp new file mode 100644 index 000000000..90f27391c Binary files /dev/null and b/docs/images/guides/tracking-job-applications/applications-list-view.webp differ diff --git a/docs/images/guides/tracking-job-applications/board-card-move-to-menu.webp b/docs/images/guides/tracking-job-applications/board-card-move-to-menu.webp new file mode 100644 index 000000000..c149da522 Binary files /dev/null and b/docs/images/guides/tracking-job-applications/board-card-move-to-menu.webp differ diff --git a/docs/images/guides/tracking-job-applications/list-bulk-actions-bar.webp b/docs/images/guides/tracking-job-applications/list-bulk-actions-bar.webp new file mode 100644 index 000000000..80267c04a Binary files /dev/null and b/docs/images/guides/tracking-job-applications/list-bulk-actions-bar.webp differ diff --git a/docs/images/guides/tracking-job-applications/screenshot-1.webp b/docs/images/guides/tracking-job-applications/screenshot-1.webp deleted file mode 100644 index c393619c6..000000000 Binary files a/docs/images/guides/tracking-job-applications/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/tracking-job-applications/screenshot-2.webp b/docs/images/guides/tracking-job-applications/screenshot-2.webp deleted file mode 100644 index d8ca060a3..000000000 Binary files a/docs/images/guides/tracking-job-applications/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/tracking-job-applications/screenshot-3.webp b/docs/images/guides/tracking-job-applications/screenshot-3.webp deleted file mode 100644 index 801404dfd..000000000 Binary files a/docs/images/guides/tracking-job-applications/screenshot-3.webp and /dev/null differ diff --git a/docs/images/guides/tracking-job-applications/screenshot-4.webp b/docs/images/guides/tracking-job-applications/screenshot-4.webp deleted file mode 100644 index c99e2b1d1..000000000 Binary files a/docs/images/guides/tracking-job-applications/screenshot-4.webp and /dev/null differ diff --git a/docs/images/guides/undoing-changes-and-version-history/history-after-restoring.webp b/docs/images/guides/undoing-changes-and-version-history/history-after-restoring.webp new file mode 100644 index 000000000..148dad4a8 Binary files /dev/null and b/docs/images/guides/undoing-changes-and-version-history/history-after-restoring.webp differ diff --git a/docs/images/guides/undoing-changes-and-version-history/history-tab.webp b/docs/images/guides/undoing-changes-and-version-history/history-tab.webp new file mode 100644 index 000000000..6087072f2 Binary files /dev/null and b/docs/images/guides/undoing-changes-and-version-history/history-tab.webp differ diff --git a/docs/images/guides/undoing-changes-and-version-history/history-viewing-a-named-version.webp b/docs/images/guides/undoing-changes-and-version-history/history-viewing-a-named-version.webp new file mode 100644 index 000000000..3edfc4d52 Binary files /dev/null and b/docs/images/guides/undoing-changes-and-version-history/history-viewing-a-named-version.webp differ diff --git a/docs/images/guides/updating-your-profile/profile-section.webp b/docs/images/guides/updating-your-profile/profile-section.webp new file mode 100644 index 000000000..3c5e1cc9b Binary files /dev/null and b/docs/images/guides/updating-your-profile/profile-section.webp differ diff --git a/docs/images/guides/updating-your-profile/update-password-dialog.webp b/docs/images/guides/updating-your-profile/update-password-dialog.webp new file mode 100644 index 000000000..47d14f050 Binary files /dev/null and b/docs/images/guides/updating-your-profile/update-password-dialog.webp differ diff --git a/docs/images/guides/using-ai-agent/screenshot-1.webp b/docs/images/guides/using-ai-agent/screenshot-1.webp deleted file mode 100644 index eeedc6e40..000000000 Binary files a/docs/images/guides/using-ai-agent/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/using-ai-in-the-builder/screenshot-1.webp b/docs/images/guides/using-ai-in-the-builder/screenshot-1.webp deleted file mode 100644 index 8a4555ee3..000000000 Binary files a/docs/images/guides/using-ai-in-the-builder/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/using-ai/add-provider-dialog.webp b/docs/images/guides/using-ai/add-provider-dialog.webp new file mode 100644 index 000000000..9946de580 Binary files /dev/null and b/docs/images/guides/using-ai/add-provider-dialog.webp differ diff --git a/docs/images/guides/using-ai/assistant-connect-provider.webp b/docs/images/guides/using-ai/assistant-connect-provider.webp new file mode 100644 index 000000000..a04f74eb5 Binary files /dev/null and b/docs/images/guides/using-ai/assistant-connect-provider.webp differ diff --git a/docs/images/guides/using-ai/edit-provider-dialog.webp b/docs/images/guides/using-ai/edit-provider-dialog.webp new file mode 100644 index 000000000..66b8329af Binary files /dev/null and b/docs/images/guides/using-ai/edit-provider-dialog.webp differ diff --git a/docs/images/guides/using-ai/provider-row-connected.webp b/docs/images/guides/using-ai/provider-row-connected.webp new file mode 100644 index 000000000..734ae9ea3 Binary files /dev/null and b/docs/images/guides/using-ai/provider-row-connected.webp differ diff --git a/docs/images/guides/using-ai/screenshot-1.webp b/docs/images/guides/using-ai/screenshot-1.webp deleted file mode 100644 index 9d9f9e52d..000000000 Binary files a/docs/images/guides/using-ai/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/using-ai/screenshot-2.webp b/docs/images/guides/using-ai/screenshot-2.webp deleted file mode 100644 index 6ff89f0de..000000000 Binary files a/docs/images/guides/using-ai/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/using-custom-styles/screenshot-2.webp b/docs/images/guides/using-custom-styles/screenshot-2.webp deleted file mode 100644 index 4fa179db6..000000000 Binary files a/docs/images/guides/using-custom-styles/screenshot-2.webp and /dev/null differ diff --git a/docs/images/guides/using-custom-styles/screenshot-3.webp b/docs/images/guides/using-custom-styles/screenshot-3.webp deleted file mode 100644 index d6b08fd7e..000000000 Binary files a/docs/images/guides/using-custom-styles/screenshot-3.webp and /dev/null differ diff --git a/docs/images/guides/using-custom-styles/screenshot-4.webp b/docs/images/guides/using-custom-styles/screenshot-4.webp deleted file mode 100644 index 48c2cb0a1..000000000 Binary files a/docs/images/guides/using-custom-styles/screenshot-4.webp and /dev/null differ diff --git a/docs/images/guides/using-custom-styles/screenshot-5.webp b/docs/images/guides/using-custom-styles/screenshot-5.webp deleted file mode 100644 index 0e4a3fa41..000000000 Binary files a/docs/images/guides/using-custom-styles/screenshot-5.webp and /dev/null differ diff --git a/docs/images/guides/using-private-notes/notes-dialog.webp b/docs/images/guides/using-private-notes/notes-dialog.webp new file mode 100644 index 000000000..ffbfe4196 Binary files /dev/null and b/docs/images/guides/using-private-notes/notes-dialog.webp differ diff --git a/docs/images/guides/using-private-notes/screenshot-1.webp b/docs/images/guides/using-private-notes/screenshot-1.webp deleted file mode 100644 index 08b358fbf..000000000 Binary files a/docs/images/guides/using-private-notes/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/using-the-api/api-key-shown-once.webp b/docs/images/guides/using-the-api/api-key-shown-once.webp new file mode 100644 index 000000000..06f92af69 Binary files /dev/null and b/docs/images/guides/using-the-api/api-key-shown-once.webp differ diff --git a/docs/images/guides/using-the-api/api-keys-section.webp b/docs/images/guides/using-the-api/api-keys-section.webp new file mode 100644 index 000000000..e136df799 Binary files /dev/null and b/docs/images/guides/using-the-api/api-keys-section.webp differ diff --git a/docs/images/guides/using-the-api/new-api-key-dialog.webp b/docs/images/guides/using-the-api/new-api-key-dialog.webp new file mode 100644 index 000000000..8bb78d144 Binary files /dev/null and b/docs/images/guides/using-the-api/new-api-key-dialog.webp differ diff --git a/docs/images/guides/using-the-api/revoke-key-undo-toast.webp b/docs/images/guides/using-the-api/revoke-key-undo-toast.webp new file mode 100644 index 000000000..7768b5094 Binary files /dev/null and b/docs/images/guides/using-the-api/revoke-key-undo-toast.webp differ diff --git a/docs/images/guides/using-the-assistant/assistant-beside-the-page.webp b/docs/images/guides/using-the-assistant/assistant-beside-the-page.webp new file mode 100644 index 000000000..f06eb84b7 Binary files /dev/null and b/docs/images/guides/using-the-assistant/assistant-beside-the-page.webp differ diff --git a/docs/images/guides/using-the-assistant/clarifying-question-card.webp b/docs/images/guides/using-the-assistant/clarifying-question-card.webp new file mode 100644 index 000000000..820acff18 Binary files /dev/null and b/docs/images/guides/using-the-assistant/clarifying-question-card.webp differ diff --git a/docs/images/guides/using-the-assistant/model-menu.webp b/docs/images/guides/using-the-assistant/model-menu.webp new file mode 100644 index 000000000..813c97fc9 Binary files /dev/null and b/docs/images/guides/using-the-assistant/model-menu.webp differ diff --git a/docs/images/guides/using-the-assistant/past-conversations.webp b/docs/images/guides/using-the-assistant/past-conversations.webp new file mode 100644 index 000000000..7dcf9f870 Binary files /dev/null and b/docs/images/guides/using-the-assistant/past-conversations.webp differ diff --git a/docs/images/guides/using-the-assistant/proposed-edit-card.webp b/docs/images/guides/using-the-assistant/proposed-edit-card.webp new file mode 100644 index 000000000..0da0e8710 Binary files /dev/null and b/docs/images/guides/using-the-assistant/proposed-edit-card.webp differ diff --git a/docs/images/guides/using-the-assistant/proposed-edit-on-page.webp b/docs/images/guides/using-the-assistant/proposed-edit-on-page.webp new file mode 100644 index 000000000..bba714540 Binary files /dev/null and b/docs/images/guides/using-the-assistant/proposed-edit-on-page.webp differ diff --git a/docs/images/guides/using-the-ats-checker/as-software-reads-it.webp b/docs/images/guides/using-the-ats-checker/as-software-reads-it.webp new file mode 100644 index 000000000..7c727d34c Binary files /dev/null and b/docs/images/guides/using-the-ats-checker/as-software-reads-it.webp differ diff --git a/docs/images/guides/using-the-ats-checker/checker-result.webp b/docs/images/guides/using-the-ats-checker/checker-result.webp new file mode 100644 index 000000000..31c41e646 Binary files /dev/null and b/docs/images/guides/using-the-ats-checker/checker-result.webp differ diff --git a/docs/images/guides/using-the-ats-checker/drop-zone-and-posting.webp b/docs/images/guides/using-the-ats-checker/drop-zone-and-posting.webp new file mode 100644 index 000000000..f938495f3 Binary files /dev/null and b/docs/images/guides/using-the-ats-checker/drop-zone-and-posting.webp differ diff --git a/docs/images/guides/using-the-ats-checker/report-and-fix.webp b/docs/images/guides/using-the-ats-checker/report-and-fix.webp new file mode 100644 index 000000000..13417fe9f Binary files /dev/null and b/docs/images/guides/using-the-ats-checker/report-and-fix.webp differ diff --git a/docs/images/guides/using-the-builder-dock/screenshot-1.webp b/docs/images/guides/using-the-builder-dock/screenshot-1.webp deleted file mode 100644 index ede330ba4..000000000 Binary files a/docs/images/guides/using-the-builder-dock/screenshot-1.webp and /dev/null differ diff --git a/docs/images/guides/using-the-command-bar/command-bar-ask.webp b/docs/images/guides/using-the-command-bar/command-bar-ask.webp new file mode 100644 index 000000000..8febf2603 Binary files /dev/null and b/docs/images/guides/using-the-command-bar/command-bar-ask.webp differ diff --git a/docs/images/guides/using-the-command-bar/command-bar-root.webp b/docs/images/guides/using-the-command-bar/command-bar-root.webp new file mode 100644 index 000000000..6bf5f8786 Binary files /dev/null and b/docs/images/guides/using-the-command-bar/command-bar-root.webp differ diff --git a/docs/images/guides/using-the-mcp-server/mcp-server-settings.webp b/docs/images/guides/using-the-mcp-server/mcp-server-settings.webp new file mode 100644 index 000000000..a603d1878 Binary files /dev/null and b/docs/images/guides/using-the-mcp-server/mcp-server-settings.webp differ diff --git a/docs/images/guides/using-the-mcp-server/oauth-consent.webp b/docs/images/guides/using-the-mcp-server/oauth-consent.webp new file mode 100644 index 000000000..3c0af2119 Binary files /dev/null and b/docs/images/guides/using-the-mcp-server/oauth-consent.webp differ diff --git a/docs/images/guides/using-the-trash/delete-now-confirmation.webp b/docs/images/guides/using-the-trash/delete-now-confirmation.webp new file mode 100644 index 000000000..af22f7020 Binary files /dev/null and b/docs/images/guides/using-the-trash/delete-now-confirmation.webp differ diff --git a/docs/images/guides/using-the-trash/moved-to-trash-toast.webp b/docs/images/guides/using-the-trash/moved-to-trash-toast.webp new file mode 100644 index 000000000..5471480e1 Binary files /dev/null and b/docs/images/guides/using-the-trash/moved-to-trash-toast.webp differ diff --git a/docs/images/guides/using-the-trash/trash-in-sidebar.webp b/docs/images/guides/using-the-trash/trash-in-sidebar.webp new file mode 100644 index 000000000..bb6704802 Binary files /dev/null and b/docs/images/guides/using-the-trash/trash-in-sidebar.webp differ diff --git a/docs/images/guides/using-the-trash/trash-page-row-menu.webp b/docs/images/guides/using-the-trash/trash-page-row-menu.webp new file mode 100644 index 000000000..61839944b Binary files /dev/null and b/docs/images/guides/using-the-trash/trash-page-row-menu.webp differ diff --git a/docs/images/guides/viewing-application-insights/insights-funnel-and-reply-figures.webp b/docs/images/guides/viewing-application-insights/insights-funnel-and-reply-figures.webp new file mode 100644 index 000000000..29c65897b Binary files /dev/null and b/docs/images/guides/viewing-application-insights/insights-funnel-and-reply-figures.webp differ diff --git a/docs/images/guides/viewing-application-insights/insights-pipeline-chart.webp b/docs/images/guides/viewing-application-insights/insights-pipeline-chart.webp new file mode 100644 index 000000000..43cd878af Binary files /dev/null and b/docs/images/guides/viewing-application-insights/insights-pipeline-chart.webp differ diff --git a/docs/images/guides/viewing-application-insights/insights-weekly-and-source-charts.webp b/docs/images/guides/viewing-application-insights/insights-weekly-and-source-charts.webp new file mode 100644 index 000000000..d21ff2cfa Binary files /dev/null and b/docs/images/guides/viewing-application-insights/insights-weekly-and-source-charts.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/application-write-a-letter.webp b/docs/images/guides/writing-a-cover-letter/application-write-a-letter.webp new file mode 100644 index 000000000..2e8a1e09d Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/application-write-a-letter.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-body-empty.webp b/docs/images/guides/writing-a-cover-letter/letter-body-empty.webp new file mode 100644 index 000000000..52a316102 Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-body-empty.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-design-matching.webp b/docs/images/guides/writing-a-cover-letter/letter-design-matching.webp new file mode 100644 index 000000000..36f3e4451 Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-design-matching.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-design-own.webp b/docs/images/guides/writing-a-cover-letter/letter-design-own.webp new file mode 100644 index 000000000..45a22ad1c Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-design-own.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-editor-write.webp b/docs/images/guides/writing-a-cover-letter/letter-editor-write.webp new file mode 100644 index 000000000..a7d4c4ef1 Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-editor-write.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-from-resume.webp b/docs/images/guides/writing-a-cover-letter/letter-from-resume.webp new file mode 100644 index 000000000..bf1222c9a Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-from-resume.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-length-and-design.webp b/docs/images/guides/writing-a-cover-letter/letter-length-and-design.webp new file mode 100644 index 000000000..02c3bd131 Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-length-and-design.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/letter-share-download.webp b/docs/images/guides/writing-a-cover-letter/letter-share-download.webp new file mode 100644 index 000000000..e81f5842f Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/letter-share-download.webp differ diff --git a/docs/images/guides/writing-a-cover-letter/new-dialog-cover-letter.webp b/docs/images/guides/writing-a-cover-letter/new-dialog-cover-letter.webp new file mode 100644 index 000000000..12b1a51f4 Binary files /dev/null and b/docs/images/guides/writing-a-cover-letter/new-dialog-cover-letter.webp differ diff --git a/docs/images/self-hosting/examples/add-provider-local-ollama.webp b/docs/images/self-hosting/examples/add-provider-local-ollama.webp new file mode 100644 index 000000000..8beda19b1 Binary files /dev/null and b/docs/images/self-hosting/examples/add-provider-local-ollama.webp differ diff --git a/docs/images/templates/azurill.webp b/docs/images/templates/azurill.webp index d6f430075..dccfd3d4f 100644 Binary files a/docs/images/templates/azurill.webp and b/docs/images/templates/azurill.webp differ diff --git a/docs/images/templates/bronzor.webp b/docs/images/templates/bronzor.webp index f1b0e0117..74a34e2b7 100644 Binary files a/docs/images/templates/bronzor.webp and b/docs/images/templates/bronzor.webp differ diff --git a/docs/images/templates/chikorita.webp b/docs/images/templates/chikorita.webp index 5bbd5d37b..b9dc007e3 100644 Binary files a/docs/images/templates/chikorita.webp and b/docs/images/templates/chikorita.webp differ diff --git a/docs/images/templates/ditgar.webp b/docs/images/templates/ditgar.webp index 3ebe327ca..223689458 100644 Binary files a/docs/images/templates/ditgar.webp and b/docs/images/templates/ditgar.webp differ diff --git a/docs/images/templates/ditto.webp b/docs/images/templates/ditto.webp index 4b933b7d0..1ea042e0f 100644 Binary files a/docs/images/templates/ditto.webp and b/docs/images/templates/ditto.webp differ diff --git a/docs/images/templates/gengar.webp b/docs/images/templates/gengar.webp index 6a183d0d3..a1c146853 100644 Binary files a/docs/images/templates/gengar.webp and b/docs/images/templates/gengar.webp differ diff --git a/docs/images/templates/glalie.webp b/docs/images/templates/glalie.webp index e3ec1062a..f1cafd31e 100644 Binary files a/docs/images/templates/glalie.webp and b/docs/images/templates/glalie.webp differ diff --git a/docs/images/templates/kakuna.webp b/docs/images/templates/kakuna.webp index 96c49d10b..7eafcfb76 100644 Binary files a/docs/images/templates/kakuna.webp and b/docs/images/templates/kakuna.webp differ diff --git a/docs/images/templates/lapras.webp b/docs/images/templates/lapras.webp index 7711197ff..64236a954 100644 Binary files a/docs/images/templates/lapras.webp and b/docs/images/templates/lapras.webp differ diff --git a/docs/images/templates/leafish.webp b/docs/images/templates/leafish.webp index f35b96af5..dfe1ce9fe 100644 Binary files a/docs/images/templates/leafish.webp and b/docs/images/templates/leafish.webp differ diff --git a/docs/images/templates/meowth.webp b/docs/images/templates/meowth.webp index 9208d69c9..3a7af5500 100644 Binary files a/docs/images/templates/meowth.webp and b/docs/images/templates/meowth.webp differ diff --git a/docs/images/templates/onyx.webp b/docs/images/templates/onyx.webp index 9f10946d0..420bfbc37 100644 Binary files a/docs/images/templates/onyx.webp and b/docs/images/templates/onyx.webp differ diff --git a/docs/images/templates/pikachu.webp b/docs/images/templates/pikachu.webp index 31bdf4e9a..3a200cd4c 100644 Binary files a/docs/images/templates/pikachu.webp and b/docs/images/templates/pikachu.webp differ diff --git a/docs/images/templates/rhyhorn.webp b/docs/images/templates/rhyhorn.webp index 7d444fde8..333287c1d 100644 Binary files a/docs/images/templates/rhyhorn.webp and b/docs/images/templates/rhyhorn.webp differ diff --git a/docs/images/templates/scizor.webp b/docs/images/templates/scizor.webp index d8cde8965..d0bd27917 100644 Binary files a/docs/images/templates/scizor.webp and b/docs/images/templates/scizor.webp differ diff --git a/docs/legal/license.mdx b/docs/legal/license.mdx index 9118b47d2..d3fd4399c 100644 --- a/docs/legal/license.mdx +++ b/docs/legal/license.mdx @@ -17,7 +17,7 @@ For the upstream repository, see: [reactive-resume/reactive-resume](https://gith MIT License -Copyright (c) 2023 Amruth Pillai +Copyright (c) 2026 Amruth Pillai Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/legal/privacy-policy.mdx b/docs/legal/privacy-policy.mdx index 031d61808..130edc4b6 100644 --- a/docs/legal/privacy-policy.mdx +++ b/docs/legal/privacy-policy.mdx @@ -28,11 +28,12 @@ If you are self-hosting, **you** are the Service Operator and responsible for co Reactive Resume is a resume builder that lets you: -- Create and edit resumes in a browser-based builder -- Store resumes in an account, optionally mark them public, and share them via a link -- Export/print resumes to PDF and generate preview screenshots +- Create and edit resumes and cover letters in a browser-based editor +- Store documents in an account, optionally make a resume public, and share it via a link +- Export resumes and cover letters (for example, to PDF, DOCX, Markdown, or JSON) +- Track job applications, including notes, contacts, interviews, and the documents you sent - Upload files such as profile pictures (and other assets used in a resume) -- Optionally configure AI features (e.g., using OpenAI/Gemini/Anthropic) from your own device +- Optionally connect your own AI provider (e.g., OpenAI, Anthropic, Google Gemini, or another supported provider) to use AI features such as the assistant --- @@ -45,7 +46,7 @@ When you create an account or sign in, the Service stores: - **Identity and profile**: name, email address, username/display username, optional profile image - **Authentication state**: whether email is verified; whether two-factor authentication is enabled -If you use social sign-in (e.g., Google, GitHub, or a custom OAuth provider), the Service stores identifiers and tokens needed to link and maintain that login. +If you use social sign-in (e.g., Google, GitHub, LinkedIn, or a custom OAuth provider), the Service stores identifiers and tokens needed to link and maintain that login. ### Authentication and security data @@ -58,21 +59,26 @@ To keep your account secure and keep you signed in, the Service stores: The Service Operator may also send **transactional emails** (for example, password reset or email verification). Depending on deployment, these emails may be delivered via an email provider or (in development/testing) the links may be logged to server output. -### Resume content +### Resume and cover letter content -When you create or import a resume, the Service stores the resume data you provide, which may include personal data such as: +When you create or import a resume or cover letter, the Service stores the data you provide, which may include personal data such as: - Contact details, location, summary - Employment, education, projects, links, and other resume sections -- Any other content you add (including rich text) +- Cover letter text and recipient details +- Any other content you add (including rich text and private notes) -Resumes may also have metadata such as tags, a slug, visibility (public/private), and an optional resume password (if you lock a resume). +Resumes may also have metadata such as tags, a slug, visibility (public/private), and an optional resume password (if you password-protect a resume). The Service also keeps a version history of your documents so you can restore earlier versions. + +### Job applications + +If you use the application tracker, the Service stores the application details you enter or import, such as company, role, job posting details, stages, interviews, notes, contacts, and which resume or cover letter you sent. ### Public resume access and statistics If you publish a resume, other users may access it via its public link. The Service may also maintain simple statistics such as: -- View count and download count +- View count and download count, including daily totals - Last viewed/downloaded timestamps ### Uploaded files (e.g., profile pictures) @@ -80,7 +86,8 @@ If you publish a resume, other users may access it via its public link. The Serv If you upload files, the Service stores them either: - On the **local filesystem** of the server (default: under a `data/` directory), or -- In **S3-compatible object storage**, if configured by the Service Operator +- In **S3-compatible object storage**, if configured by the Service Operator, or +- In **Vercel Blob** storage, when the Service runs on Vercel Depending on configuration, uploaded files may be publicly accessible (for example, some S3 configurations may default to public read access for uploaded objects). The Service Operator is responsible for selecting appropriate access controls for uploads. @@ -89,14 +96,14 @@ Depending on configuration, uploaded files may be publicly accessible (for examp If the Service Operator enables API key functionality, the Service can store: - API key metadata and rate limit counters -- The API key value itself (as stored by the Service) +- A hashed form of the API key, used to verify it ### Local-only preferences and settings Some settings are stored on your device: - **Cookies**: UI preferences such as `theme` and `locale` -- **Local storage**: some client-side state and, if you enable AI features, your **AI provider configuration and API key** may be stored in your browser's local storage +- **Local storage**: some client-side state, such as view preferences and unsaved drafts These local-only values are stored in your browser and are not necessarily transmitted to the Service Operator unless you choose to use related features. @@ -108,7 +115,7 @@ We use the information above to: - Provide and operate the Service (account access, resume editing, storage, sharing) - Authenticate users and prevent abuse/fraud (sessions, security logs/metadata) -- Generate PDFs and screenshots you request +- Generate PDFs, previews, and other exports you request - Maintain basic functionality such as localization and theme preferences - Provide support and respond to user requests (if you contact the Service Operator) @@ -129,21 +136,21 @@ The Service does not include built-in behavioral advertising or third-party anal We share information only as needed to provide the Service: -### PDF generation (client-side) +### PDF generation -When you export to PDF, the Service renders the document directly in your browser using the Forme PDF engine, which runs in your browser as WebAssembly. Resume content is processed locally on your device for this purpose and is not sent to a separate rendering service. No third-party "printer" or headless-browser service is involved in the export. +When you download a PDF from the editor, the Service renders the document directly in your browser using the Forme PDF engine, which runs in your browser as WebAssembly. Resume content is processed locally on your device for this purpose. When a visitor downloads the PDF of a public resume, or when a PDF is requested through the API, the Service's own server renders it with the same engine. No third-party "printer" or headless-browser service is involved in either case. ### Storage providers (optional) -If configured, uploaded files may be stored in an S3-compatible provider. In that case, the storage provider processes and stores file data on behalf of the Service Operator. +If configured, uploaded files may be stored in an S3-compatible provider or in Vercel Blob. In that case, the storage provider processes and stores file data on behalf of the Service Operator. ### OAuth providers (optional) -If you sign in via OAuth (Google/GitHub/custom), those providers receive authentication requests and return profile information (such as email/name) to the Service, as permitted by your provider settings. +If you sign in via OAuth (Google/GitHub/LinkedIn/custom), those providers receive authentication requests and return profile information (such as email/name) to the Service, as permitted by your provider settings. ### AI providers (optional, user-supplied) -If you enable AI features and provide your own API key, prompts and generated content may be sent to your selected AI provider (OpenAI, Google, Anthropic), according to your use of those features and the provider's policies. +If you enable AI features and provide your own API key, the Service stores your provider settings and stores the API key encrypted. When you use AI features, the Service sends prompts, the relevant document content, and any files you attach to your selected AI provider (for example, OpenAI, Anthropic, or Google Gemini), according to your use of those features and the provider's policies. The Service stores your assistant conversations, attachments, and proposed changes in your account until you delete them or your account. --- @@ -156,7 +163,7 @@ As a baseline: - Account data and resumes are retained until you delete them (or your account is deleted). - Session and security data may be retained as needed for authentication and security. - Uploaded files are retained until deleted (for example, when you remove a picture or delete a resume/account). -- Cached screenshot artifacts may be retained briefly (for example, minutes) for performance. +- Documents you move to the Trash are deleted permanently after 30 days, unless you restore or delete them sooner. The Service Operator may also retain backups and logs for limited periods. diff --git a/docs/legal/terms-of-service.mdx b/docs/legal/terms-of-service.mdx index 68574c024..f05a15d9e 100644 --- a/docs/legal/terms-of-service.mdx +++ b/docs/legal/terms-of-service.mdx @@ -24,7 +24,7 @@ If you do not agree, do not use the Service. ## The Service -Reactive Resume is a resume builder that allows you to create, store, and share resumes, upload related assets, and export/print resumes (including generating PDFs and screenshots). +Reactive Resume is a resume builder that allows you to create, store, and share resumes and cover letters, track job applications, upload related assets, and export resumes (including generating PDFs). --- @@ -82,7 +82,7 @@ The Service Operator may remove Content or restrict access if needed to enforce ## Uploads, storage, and delivery -Uploaded files may be stored on the Service Operator's infrastructure and may be served back to you (and, if your resume is public, to others). Depending on configuration, storage may be local filesystem storage or S3-compatible object storage. +Uploaded files may be stored on the Service Operator's infrastructure and may be served back to you (and, if your resume is public, to others). Depending on configuration, storage may be local filesystem storage, S3-compatible object storage, or Vercel Blob. You represent that you have the rights necessary to upload and use any files and that doing so does not violate any law or third-party rights. @@ -90,7 +90,7 @@ You represent that you have the rights necessary to upload and use any files and ## Exports (PDF) -When you request a PDF export, the Service renders the document in your own browser using the Forme PDF engine. Your resume content is processed locally by the JavaScript running on your device; it is not transmitted to a separate rendering service for the purpose of generating the PDF. +When you download a PDF from the editor, the Service renders the document in your own browser using the Forme PDF engine; your resume content is processed locally on your device. PDFs of public resumes and PDFs requested through the API are rendered by the Service's own server with the same engine. In neither case is your content transmitted to a separate third-party rendering service. --- @@ -98,9 +98,9 @@ When you request a PDF export, the Service renders the document in your own brow The Service may integrate with third parties depending on configuration and your choices, including: -- OAuth providers (e.g., Google/GitHub/custom OAuth) for sign-in -- Storage providers (S3-compatible) -- AI providers (OpenAI, Google, Anthropic) if you enable AI features and provide your own API key +- OAuth providers (e.g., Google/GitHub/LinkedIn/custom OAuth) for sign-in +- Storage providers (S3-compatible or Vercel Blob) +- AI providers (for example, OpenAI, Anthropic, or Google Gemini) if you enable AI features and provide your own API key Your use of third-party services may be subject to their own terms and policies. The Service Operator is not responsible for third-party services outside its control. diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx index bfd9db7c3..37c4b9f2b 100644 --- a/docs/self-hosting/docker.mdx +++ b/docs/self-hosting/docker.mdx @@ -1,623 +1,369 @@ --- title: "Self-hosting with Docker" -description: "How to self-host Reactive Resume with Docker (Postgres only), with an environment variable reference and troubleshooting tips." +description: "Run your own Reactive Resume server with Docker Compose and PostgreSQL: set up, create the first account, check health, update and back up." --- - - **From v5.1.0 onwards** — PDF generation now runs entirely client-side with the Forme PDF engine (WebAssembly). New deployments no longer require Browserless, Chromium, or any external print service as a dependency. The `PRINTER_*` and `BROWSERLESS_*` environment variables are no longer read and can be removed from your `.env`. - +This guide sets up your own Reactive Resume server with Docker Compose. You end up with two containers, the app and a +PostgreSQL database, reachable at an address you choose. It's the same app that runs on `https://rxresu.me`, and your data stays on +your hardware. -## Overview +## Before you start -Reactive Resume can be self-hosted with Docker. These are the services you'll need: +You need: -The official image runs one application container that serves both the web app and API. PostgreSQL must run as a -separate service and the app connects to it through `DATABASE_URL`; no all-in-one image with an embedded database is -planned. Follow the [Docker Compose quickstart](#quickstart-using-docker-compose) below for the supported setup. +- A Linux, macOS or Windows host with Docker Engine and the Docker Compose plugin (or Docker Desktop). +- At least 1 vCPU and 1 GB of memory for the app. Allow 2 GB when PostgreSQL runs on the same host. +- Disk space for the database and uploaded pictures. 10 GB is plenty to start. +- For anything beyond a test on your own machine: a domain name and a reverse proxy that serves it over HTTPS. See + [Deployment examples](/self-hosting/examples) for Traefik, Caddy and nginx. - - Stores accounts, resumes, and application data. - - SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console. - - - Use S3-compatible storage, or local persistent storage via /app/data. - - +## How the pieces fit -You can pull the latest app image from: +The official image runs a single Node.js process on port `3000`. It serves the web app, the API, the MCP server and +uploaded files from one address. PDFs are rendered by the app itself, in the browser or on the server, so there is no +separate printer service. -- Docker Hub: `amruthpillai/reactive-resume:latest` -- GitHub Container Registry: `ghcr.io/reactive-resume/reactive-resume:latest` +| Service | Required | Purpose | +| --- | --- | --- | +| PostgreSQL | Yes | Stores accounts, resumes, cover letters and applications. Runs as its own container or managed database. | +| Upload storage | Yes | A folder mounted at `/app/data`, or an S3-compatible bucket instead. | +| SMTP server | No | Sends verification and password reset emails. Without it, emails are written to the app log. | +| Redis | No | Shares state between several app processes and lets Assistant replies survive a page reload. | -## Minimum requirements +The image is published to two registries with the same tags: - - Docker Engine + Docker Compose plugin (or Docker Desktop). - 1 vCPU / 1 GB RAM minimum (2 GB recommended if Postgres runs on the same host). - Enough for Postgres + uploads (start with 10-20 GB and scale as needed). - +- Docker Hub: `amruthpillai/reactive-resume` +- GitHub Container Registry: `ghcr.io/reactive-resume/reactive-resume` -## Smallest supported setup +| Tag | Contents | +| --- | --- | +| `latest` | The newest release. | +| `v6`, `v6.0`, `v6.0.0` | A major, minor or exact release. Pin `v6` to get fixes without jumping to the next major version. | +| `nightly` | The current `main` branch. For testing only. | -1. Provide a separate, healthy PostgreSQL service. In the example below, its service name is `postgres`. -2. Put `APP_URL`, `DATABASE_URL`, and `AUTH_SECRET` in a private `.env` file. Set the database host in `DATABASE_URL` - to a name or address reachable from the app container. -3. If S3 is disabled, mount persistent storage for app uploads at `/app/data`. -4. Attach the `reactive-resume` app service and PostgreSQL service to the intended private container network. Do not - expose PostgreSQL to the public internet. -5. Launch the services with the [Docker Compose quickstart](#quickstart-using-docker-compose) below. -6. Wait for PostgreSQL, automatic migrations, and the app health check before opening the UI. +Images are built for `linux/amd64` and `linux/arm64`, so they run on most servers, Apple silicon, and a Raspberry +Pi 4 or newer running a 64-bit operating system. -The repository's full `compose.yml` also defines optional Redis and S3-compatible storage services. Those services are -not required for the core resume workflow; use the two-service example below when you only need the app and PostgreSQL. -The repository file is a broader source-build stack and publishes administration ports for local use. Before using it -on an internet-facing host, remove those host port mappings, bind them to loopback, or restrict them with a firewall. - -## Quickstart using Docker Compose - -Create a new folder (for example `reactive-resume/`) with: - -- `compose.yml` -- `.env` -- a persistent data directory for uploads (for example `./data`) +## Set up with Docker Compose - - Start by creating a `.env` file next to your `compose.yml`. - - The Compose example below reads `.env` directly. If you use the repository's `compose.yml` instead, copy its `.env.example` into the same folder. That file supplies defaults before your `.env` overrides are applied. - -```bash .env -# --- Server --- -TZ="Etc/UTC" -APP_URL="http://localhost:3000" - -# --- Database (PostgreSQL) --- -DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres" - -# --- Authentication --- -# Generated using `openssl rand -hex 32` -AUTH_SECRET="" -# Better Auth dashboard API key (optional) -BETTER_AUTH_API_KEY="" - -# Social Auth (Google, optional) -GOOGLE_CLIENT_ID="" -GOOGLE_CLIENT_SECRET="" - -# Social Auth (GitHub, optional) -GITHUB_CLIENT_ID="" -GITHUB_CLIENT_SECRET="" - -# Social Auth (LinkedIn, optional) -LINKEDIN_CLIENT_ID="" -LINKEDIN_CLIENT_SECRET="" - -# Custom OAuth Provider -OAUTH_PROVIDER_NAME="" -OAUTH_CLIENT_ID="" -OAUTH_CLIENT_SECRET="" -# Use EITHER discovery URL (preferred for OIDC-compliant providers): -OAUTH_DISCOVERY_URL="" -# OR manual URLs (all three required if not using discovery): -OAUTH_AUTHORIZATION_URL="" -OAUTH_TOKEN_URL="" -OAUTH_USER_INFO_URL="" -# Custom scopes (space-separated, defaults to "openid profile email") -OAUTH_SCOPES="" - -# --- Email (optional) --- -# If all keys are disabled, the app logs the email to be sent to the console instead. -SMTP_HOST="" -SMTP_PORT="587" -SMTP_USER="" -SMTP_PASS="" -SMTP_FROM="Reactive Resume " -SMTP_SECURE="false" - -# --- Storage (optional) --- -# If all S3 keys are disabled, the app uses local filesystem storage instead. -# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity. -S3_ACCESS_KEY_ID="" -S3_SECRET_ACCESS_KEY="" -S3_REGION="us-east-1" -S3_ENDPOINT="" -S3_BUCKET="" -# Set to "true" for path-style URLs (https://endpoint/bucket), common with MinIO, SeaweedFS, etc. -# Set to "false" for virtual-hosted-style URLs (https://bucket.endpoint), common with AWS S3, Cloudflare R2, etc. -S3_FORCE_PATH_STYLE="false" - -# --- AI features (optional) --- -# ENCRYPTION_SECRET is required for saved AI providers. REDIS_URL is also required for the AI Agent workspace. -# The rest of Reactive Resume can run without these. -REDIS_URL="" -# Generated using `openssl rand -hex 32` -ENCRYPTION_SECRET="" - -# --- Feature Flags --- -FLAG_DISABLE_SIGNUPS="false" -FLAG_DISABLE_EMAIL_AUTH="false" -FLAG_DISABLE_IMAGE_PROCESSING="false" -FLAG_DISABLE_API_RATE_LIMIT="false" -# Allows any parseable dynamic OAuth redirect URI. Keep false unless this is a trusted self-hosted deployment. -FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI="false" -# Allows unsafe/private/non-public AI provider base URLs. Keep false unless this is a trusted self-hosted deployment. -FLAG_ALLOW_UNSAFE_AI_BASE_URL="false" -``` + + ```bash + mkdir reactive-resume && cd reactive-resume + ``` + The next steps create two files in it: `.env` for settings and `compose.yml` for the containers. - - Generate a strong secret and paste it into `AUTH_SECRET`. + + Create `.env` with the three required settings: + + ```bash .env + # The address people will use to reach your instance. + APP_URL="http://localhost:3000" + + # "postgres" is the database service name in compose.yml. + POSTGRES_PASSWORD="replace-with-a-database-password" + DATABASE_URL="postgresql://postgres:replace-with-a-database-password@postgres:5432/postgres" + + # Generate with: openssl rand -hex 32 + AUTH_SECRET="" + ``` + + Generate a value for `AUTH_SECRET` and a database password, and paste them in. Use the same password in + `POSTGRES_PASSWORD` and `DATABASE_URL`. + - ```bash Linux/macOS - openssl rand -hex 32 - ``` - - ```bash Linux/macOS (alternative) - head -c 32 /dev/urandom | hexdump -v -e '/1 "%02x"' - ``` - - ```powershell Windows - [byte[]]$bytes = New-Object byte[] 32; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); $bytes | ForEach-Object { "{0:x2}" -f $_ } | Out-String -Stream | ForEach-Object { $_.Trim() } | Write-Host -NoNewline - ``` + ```bash Linux and macOS + openssl rand -hex 32 + ``` + ```powershell Windows + # PowerShell 7 or newer + [Convert]::ToHexString([Security.Cryptography.RandomNumberGenerator]::GetBytes(32)).ToLower() + ``` + If you will serve the instance from a domain, set `APP_URL` to that address now, for example + `https://resume.example.com`. Every other setting is optional; the + [environment variable reference](/self-hosting/environment-variables) lists them all. - This setup runs Postgres and Reactive Resume on a private Docker network. - - - -```yaml compose.yml -services: - postgres: - image: postgres:17 - restart: unless-stopped - environment: - POSTGRES_DB: postgres - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - volumes: - - postgres_data:/var/lib/postgresql - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] - interval: 10s - timeout: 5s - retries: 10 - - reactive-resume: - image: amruthpillai/reactive-resume:latest - # image: ghcr.io/reactive-resume/reactive-resume:latest - restart: unless-stopped - ports: - - "3000:3000" - env_file: - - .env - volumes: - # Used when S3 is not configured; keeps uploads persistent - - ./data:/app/data - depends_on: + ```yaml compose.yml + services: postgres: - condition: service_healthy - healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then((r) => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1));"] - interval: 30s - timeout: 10s - retries: 3 + image: postgres:18 + restart: unless-stopped + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] + interval: 10s + timeout: 5s + retries: 10 -volumes: - postgres_data: -``` + reactive-resume: + image: amruthpillai/reactive-resume:latest + restart: unless-stopped + ports: + - "3000:3000" + env_file: .env + volumes: + - reactive_resume_data:/app/data + depends_on: + postgres: + condition: service_healthy - + volumes: + postgres_data: + reactive_resume_data: + ``` + + PostgreSQL is only reachable from the app container, never from outside. Uploads live in the + `reactive_resume_data` volume, so they survive when the container is recreated. The image has a built-in health + check, so Compose reports the app as `healthy` once it's ready. - Prefer pulling from Docker Hub? Keep amruthpillai/reactive-resume:latest. Prefer GHCR? Swap it to ghcr.io/reactive-resume/reactive-resume:latest. + To use GitHub Container Registry instead of Docker Hub, change the image to + `ghcr.io/reactive-resume/reactive-resume:latest`. - - - In Docker, the Reactive Resume server listens on PORT and serves both the API and the built web app. - The default image uses PORT=3000, so the example maps 3000:3000. If you change - PORT, update the container-side port mapping and health check to match. - - - - + + ```bash + docker compose up -d + docker compose logs -f reactive-resume + ``` -```bash -docker compose up -d -``` + On the first start the app creates its database tables. When the log shows a line containing + `Up and running on`, press Ctrl C to stop following the log. Within a minute, `docker compose ps` shows + the app as `healthy`. + -```bash -docker compose ps -``` + + Open your `APP_URL` in a browser and create an account; see [Creating an account](/guides/creating-an-account). -```bash -docker compose logs -f reactive-resume -``` - - - - Reactive Resume should now be available at your `APP_URL` (for the example above: `http://localhost:3000`). + You're signed in right away. Reactive Resume also sends a verification email, but you don't have to open it to + use the app. Without SMTP settings the email isn't sent; its link is written to the app log instead: + ```bash + docker compose logs reactive-resume | grep verify-email + ``` -## Unraid and other homelab platforms +Your instance is running. Next, you might want to: -Use your platform's generic container configuration to create two separately managed containers: one for Reactive -Resume and one for PostgreSQL. No official Unraid Community Applications template is provided. - -- Use the official `amruthpillai/reactive-resume:latest` or `ghcr.io/reactive-resume/reactive-resume:latest` image for the - app container. -- Map the app's container port `3000` to the host port you want to use. -- Connect both containers to a private container network. Set the host in `DATABASE_URL` to the PostgreSQL container or - service name reachable on that network. -- Set `APP_URL`, `DATABASE_URL`, and `AUTH_SECRET` as private environment variables. -- When S3 is disabled, map persistent app upload storage to `/app/data`. -- Give PostgreSQL its own persistent data volume and manage it independently from the app container. +- **Keep it private.** After your own account exists, add `FLAG_DISABLE_SIGNUPS="true"` to `.env` and run + `docker compose up -d`. Nobody else can sign up; existing accounts keep working. +- **Send real emails.** Add the `SMTP_*` settings from the [reference](/self-hosting/environment-variables#email). +- **Turn on AI features.** Add `ENCRYPTION_SECRET` (another `openssl rand -hex 32` value). People can then connect + their own AI provider and use the Assistant. See [Add Redis and turn on AI](/self-hosting/examples#add-redis-and-turn-on-ai). +- **Serve it over HTTPS.** Put a reverse proxy in front and set `APP_URL` to the public `https://` address. See + [Deployment examples](/self-hosting/examples). - `localhost` inside the Reactive Resume container refers to that app container. It cannot reach a separate PostgreSQL - container. Use the PostgreSQL container or service name on the private network instead. + `APP_URL` must match the address in the browser's address bar exactly, including `https://`. If it doesn't, sign-in + redirects go to the wrong place and session cookies don't stick. -After starting both containers, wait for PostgreSQL to become healthy and check the app logs while automatic migrations -run. Open the UI only after the app health check succeeds. +## What happens at startup -## How startup works (database migrations) +Each time the app container starts, it: - - On every start, the server automatically runs database migrations before serving traffic. If migrations fail - (usually due to a DB connection issue), the container will exit with an error. - +1. Applies any new database migrations. Several containers starting at once take turns, so only one migrates. +2. Compares the database schema with what the migrations expect. A mismatch is logged; set `STRICT_SCHEMA_CHECK=true` + to stop instead. +3. With local storage, checks that `/app/data` (or `LOCAL_STORAGE_PATH`) is writable, and stops if it isn't. +4. Starts serving on `PORT`. -## Environment variables +If step 1 or 3 fails, the container exits with the reason in its log. This is almost always a wrong `DATABASE_URL`, a +database that isn't reachable yet, or a storage folder the container can't write to. - - -
    -
  • - APP_URL -
  • -
  • - DATABASE_URL -
  • -
  • - AUTH_SECRET -
  • -
-
- -
    -
  • - SMTP (SMTP_*) -
  • -
  • - Social auth (GOOGLE_*, GITHUB_*, LINKEDIN_*,{" "} - OAUTH_*) -
  • -
  • - S3 storage (S3_*) -
  • -
  • - AI providers and AI Agent workspace (ENCRYPTION_SECRET, REDIS_URL) -
  • -
  • - Feature flags (FLAG_*) -
  • -
-
-
+## Check the health endpoint - - - - **`TZ`**: Sets the container timezone (affects logs and server-side timestamps). Recommended: `Etc/UTC`. - - **`APP_URL`**: Canonical/public URL for your instance (used for absolute URLs, redirects, and auth flows). If behind a reverse proxy, set this to your public HTTPS URL (for example, `https://resume.example.com`). - - **`PORT`**: Port the production Docker container listens on. Defaults to `3000` in the official image. If you change it, update your Compose port mapping and health check from `3000` to the new container port. - - **`SERVER_PORT`**: Used only for local development when the Vite web app and Hono server run as separate processes. It is ignored by the production Docker image. - - - - - **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In - Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`. - If your password - contains special characters (`@`, `#`, `:`), URL-encode it. - For managed Postgres, add provider-specific params (for - example `?sslmode=require`) when needed. - - - - **`AUTH_SECRET`**: Secret used to secure authentication. Changing it invalidates existing sessions. - - Generate with: - - +`GET /api/health` reports whether the app can reach its database, storage and, when configured, Redis. It answers +`200` when everything is healthy and `503` when any check fails. The image's built-in health check calls it every 30 +seconds. ```bash -openssl rand -hex 32 +curl http://localhost:3000/api/health ``` - +```json +{ + "service": "reactive-resume", + "version": "6.0.0", + "status": "healthy", + "timestamp": "2026-09-30T09:00:00.000Z", + "uptime": "3600.12s", + "database": { "status": "healthy", "latencyMs": 2 }, + "storage": { + "type": "local", + "status": "healthy", + "message": "Local filesystem storage is accessible and has read/write permission.", + "latencyMs": 1 + } +} +``` - **`GOOGLE_CLIENT_ID`** / **`GOOGLE_CLIENT_SECRET`** (optional): Enables Google sign-in. +The `redis` field appears only when `REDIS_URL` is set. When a check fails, its entry shows `"status": "unhealthy"` +and the app log has the details. Each check times out after 1.5 seconds. - **`GITHUB_CLIENT_ID`** / **`GITHUB_CLIENT_SECRET`** (optional): Enables GitHub sign-in. +Use this endpoint for load balancer or orchestrator readiness checks. Restarting the app doesn't fix a database or +storage outage, so avoid using it to kill containers. - **`LINKEDIN_CLIENT_ID`** / **`LINKEDIN_CLIENT_SECRET`** (optional): Enables LinkedIn sign-in. +## Update your instance - **`BETTER_AUTH_API_KEY`** (optional): Enables Better Auth dashboard integrations. + + + Back up the database and uploads before every update. See [Back up your data](#back-up-your-data). + - **Custom OAuth provider** (optional): - - **`OAUTH_PROVIDER_NAME`**: Display name in the UI - - **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider - - **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`) + + ```bash + docker compose pull reactive-resume + ``` + - Configure endpoints using **one** of these methods: - - **Option A (OIDC Discovery, preferred)**: Set `OAUTH_DISCOVERY_URL` to your provider's `.well-known/openid-configuration` URL - - **Option B (manual URLs)**: Set all three: `OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, and `OAUTH_USER_INFO_URL` + + ```bash + docker compose up -d reactive-resume + docker compose logs -f reactive-resume + ``` - + The new version applies its migrations on startup. PostgreSQL keeps running. + + - - If SMTP is not configured, the app logs emails to the server console instead of sending them. +Updating from v5? Read [Upgrading to v6](/self-hosting/upgrading-to-v6) first. It covers a one-time command for resumes +that still use the old style editor. - - Email delivery is enabled only when **all** of `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM` are set. - - **`SMTP_HOST`**: SMTP host (if empty, email sending is disabled). - - **`SMTP_PORT`**: Defaults to `587` in the app. - - **`SMTP_USER`** / **`SMTP_PASS`**: SMTP credentials. - - **`SMTP_FROM`**: Default from address (for example, `Reactive Resume `). - - **`SMTP_SECURE`**: `"true"` or `"false"` (string). Match your provider settings. +PostgreSQL is updated separately. Pulling a new app image never changes the database server's major version. To move +to a new major PostgreSQL version, dump the database, start the new version with an empty volume, and restore. - +## Back up your data - - - **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data` in the official image. - - Mount local uploads to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - - **`LOCAL_STORAGE_PATH`** (optional): Overrides the local data directory. Defaults to `/app/data` in the official Docker image and `/data` in development. The container validates this path is writable at startup and refuses to start otherwise. - - **Rootless Docker**: `/app/data` remains the container path. Prefer the named volume from the example Compose file, or make sure a bind-mounted host directory is writable by the container's `node` user mapping. - - **S3/S3-compatible**: Configure `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_REGION`, `S3_ENDPOINT`, and `S3_BUCKET`. - - **Agent attachments/private objects**: The AI Agent workspace requires S3-compatible storage for private objects. Local storage rejects private objects. - - **`S3_FORCE_PATH_STYLE`** controls bucket addressing (defaults to `"false"`): - - `"true"` for path-style URLs (`https://endpoint/bucket`) common with MinIO/SeaweedFS. - - `"false"` for virtual-hosted-style URLs (`https://bucket.endpoint`) common with AWS S3 / Cloudflare R2. - +Your data lives in two places. Back up both, on a schedule, and test that you can restore them. - - Saved AI provider management is usable only when **`ENCRYPTION_SECRET`** is configured. The AI Agent workspace also requires **`REDIS_URL`**. The rest of Reactive Resume can run without them. +- **Database.** Dump it with `pg_dump`: - - **`REDIS_URL`**: Redis connection string used by the AI Agent workspace. - - **`ENCRYPTION_SECRET`**: Secret used to encrypt saved AI provider credentials. Generate with `openssl rand -hex 32`. - - Live web research depends on the selected AI provider/model supporting native web search. The app does not run its own URL crawler. + ```bash + docker compose exec -T postgres pg_dump -U postgres -d postgres --format=custom > reactive-resume.dump + ``` - If you use the Postgres-only Compose example above and want the AI Agent workspace, add a Redis service or use managed Redis, then set `REDIS_URL`. - + Restore into an empty database with `pg_restore`: - - - **`FLAG_DISABLE_SIGNUPS`**: Disables new signups (web app and server). Useful for private instances. - - **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/LinkedIn/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required. - - **`FLAG_DISABLE_IMAGE_PROCESSING`**: Disables image processing. This is useful if you are using a machine with limited resources, like a Raspberry Pi. - - **`FLAG_DISABLE_API_RATE_LIMIT`**: Disables API rate limiting for authentication endpoints. Rate limiting is enabled by default in production to prevent abuse. - - **`FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`**: Allows dynamic OAuth client registration to use any parseable redirect URI, including custom schemes, private hosts, and non-loopback `http://` URLs. **Warning: enabling this on a public or multi-tenant deployment can enable phishing or token exfiltration.** Only enable on trusted, self-hosted deployments. - - **`FLAG_ALLOW_UNSAFE_AI_BASE_URL`**: Allows AI providers to be configured with unsafe, private, or non-public base URLs, including `http://` and private/loopback addresses (for example, a local Ollama instance at `http://192.168.1.10:11434`). Public HTTPS provider URLs remain the safe default. **Warning: enabling this on a multi-tenant deployment is an SSRF risk.** Only enable on trusted, self-hosted deployments. - - + ```bash + docker compose exec -T postgres pg_restore -U postgres -d postgres --clean --if-exists < reactive-resume.dump + ``` -## Updating your installation +- **Uploads.** With local storage, copy the contents of the `reactive_resume_data` volume (or your bind-mounted + folder). With S3, turn on bucket versioning or replication in your provider. -To update an installation created from the image-based quickstart above to the latest version, follow only the numbered -steps below. If you use the repository's full `compose.yml`, use the separate source-build path after these steps. +Keep a copy of your `.env` too. Without the same `AUTH_SECRET` and `ENCRYPTION_SECRET`, a restored instance signs +everyone out, breaks two-factor authentication and loses saved AI provider keys. -1. **Back up your database and uploads first.** Do this before every update. +## Build from the repository - The database and upload storage are independent resources. Recreating the app container must preserve both the - PostgreSQL data volume or managed database and the `/app/data` mount or S3 bucket. - -2. **Pull the latest app image.** Leave the PostgreSQL service unchanged. - - ```bash - docker compose pull reactive-resume - ``` - -3. **Recreate only the app container** to run the new image. - - ```bash - docker compose up -d --no-deps reactive-resume - ``` - -4. **Check migration/startup logs** after deploy. - - ```bash - docker compose logs -f reactive-resume - ``` - -5. **(Optional) Remove old, unused Docker images** to free up disk space. - ```bash - docker image prune -f - ``` - -### Update from the repository Compose file - -The repository's full `compose.yml` names its build-only app service `reactive_resume`. After confirming its dependencies -are healthy, rebuild that service and follow its migration/startup logs with: +The repository's own `compose.yml` builds the image from source and also starts Redis and SeaweedFS (S3-compatible +storage). It reads `.env.example` first and then your `.env`. ```bash +git clone https://github.com/reactive-resume/reactive-resume.git +cd reactive-resume +cp .env.example .env # then set APP_URL, AUTH_SECRET and ENCRYPTION_SECRET +docker compose up -d --build +``` + +To update, pull the latest code and rebuild only the app service: + +```bash +git pull docker compose up -d --build --no-deps reactive_resume -docker compose logs -f reactive_resume ``` -Do not run `docker compose pull` for this build-only service. + + The repository file publishes PostgreSQL (`5432`) and SeaweedFS (`8333`) on the host, with default passwords. Remove + those `ports` entries or bind them to `127.0.0.1` before using it on a server reachable from the internet. + -This process updates the app container and automatically runs DB migrations on startup. If migration fails, restore from backup and fix configuration before retrying. +## Unraid, Synology and other homelab platforms -Update PostgreSQL separately from the app. Choose a supported, major-pinned PostgreSQL image or select the target version -through your managed provider, then follow that image's, host's, or provider's upgrade procedure. Back up the database -and verify that the backup can be restored before a major-version upgrade. Pulling a new app image and running app -migrations do not upgrade the PostgreSQL server. +There is no official app template. Use your platform's generic container settings: -### Converting styles from the old style editor (one time) +- Create two containers: one from `amruthpillai/reactive-resume:latest` and one from `postgres:18`. Give PostgreSQL its + own persistent volume at `/var/lib/postgresql`. +- Put both containers on the same private network, and use the PostgreSQL container's name as the host in + `DATABASE_URL`. +- Map the app's container port `3000` to any free host port. +- Map a persistent folder to `/app/data`. The app runs as the `node` user (UID 1000), so that user must be able to + write to it. +- Set `APP_URL`, `DATABASE_URL` and `AUTH_SECRET` as environment variables. -Resumes and cover letters styled with the old style editor (before Custom Styles became CSS) keep those styles only once -they're converted to Custom Styles. The conversion isn't run automatically: after updating to the first version without -the old editor, run it once from the app container. It uses the container's `DATABASE_URL`. - -1. **Dry run.** Converts every affected resume in memory and reports the counts; nothing is written. - - ```bash - docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs - ``` - -2. **Convert.** Every replaced stylesheet is saved to the backup file first. Only the stylesheet of each resume or letter - changes, and running it again (for example after an interruption) skips what's already converted. - - ```bash - docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs --apply --backup /app/data/legacy-styles-backup.ndjson - ``` - -3. **Undo, if needed.** Puts back the recorded stylesheets, except on resumes edited since. - - ```bash - docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs --restore /app/data/legacy-styles-backup.ndjson - ``` - -## Backups (recommended) - -Reactive Resume stores data in two places: the PostgreSQL database and file uploads (either local storage or S3). Back up both on a regular schedule. - -Test restores for both resources. An app container backup alone does not include the separate database or uploads, and -recreating the app container must not replace either persistent resource. - -### Database backups - -Your PostgreSQL database holds all user accounts, resumes, and application data. Use `pg_dump` to take periodic backups and store them somewhere secure. Many providers of managed PostgreSQL also offer automated backups that handle scheduling, retention, and restores for you. - -### Upload backups - -If you're using local storage (the `./data` directory), include this directory in your regular backup routine. A simple approach is to use `rsync` or a similar tool to copy the directory to a remote server or cloud storage. - -If you're using S3-compatible storage, consider enabling versioning on your bucket to protect against accidental deletions. Most S3 providers also support lifecycle rules for automatic cleanup of old versions and cross-region replication for disaster recovery. - -## Health checks - -Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies. It checks **database**, **storage**, and **Redis** when configured; if a configured dependency is unhealthy, the endpoint returns HTTP `503`. - -### How it works - -The Docker Compose configuration includes a health check that periodically calls the `/api/health` endpoint: - -```yaml -healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then((r) => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1));"] - interval: 30s - timeout: 10s - retries: 3 -``` - -When the health check fails, Docker marks the container as **unhealthy**. This status is visible when running `docker compose ps` or `docker ps`. - -### Reverse proxy integration - -Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream health checks) can use Docker's health status to make routing decisions: - -- **Healthy containers** receive traffic as normal -- **Unhealthy containers** are automatically removed from the load balancer pool - -This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses database or storage connectivity), the reverse proxy will stop routing traffic to it until it recovers. - - - If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy - containers are excluded from routing without any additional configuration. - - -### Manually checking health - -To check your instance yourself: - -```bash -# From outside the container -curl -f http://localhost:3000/api/health - -# Check Docker's health status -docker compose ps -``` - -A healthy response returns HTTP 200. If you get a different status code, the JSON response body says what failed. If the connection is refused or times out there is no response to read, so check the container and reverse-proxy logs instead. + + Inside the app container, `localhost` means the app container itself. It never reaches a database in another + container, so don't use `localhost` in `DATABASE_URL`. + ## Troubleshooting - - - **Common cause**: database migrations failed (often a bad `DATABASE_URL`). - - **What to do**: - Check logs for migration errors and database connectivity details: - ```bash - docker compose logs -f reactive-resume - ``` + + Read the log with `docker compose logs reactive-resume`. The last lines name the problem: + + - `Invalid environment variables`: a required variable is missing or malformed. The message names it. + - `Database migrations failed` or `ECONNREFUSED`: `DATABASE_URL` is wrong or PostgreSQL isn't reachable. Check the + host name, password (URL-encode special characters) and that PostgreSQL is healthy. + - `Local storage path is not writable`: the folder mounted at `/app/data` isn't writable by UID 1000. Fix its + owner, or use a named volume. - - - **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy), or - you're serving HTTPS while `APP_URL` is `http://...`. - **Fix**: set `APP_URL` to your canonical public HTTPS URL and - restart the container. - + + `APP_URL` doesn't match the address you're using, or you're on `https://` while `APP_URL` says `http://`. Set + `APP_URL` to the exact public address and run `docker compose up -d`. + - - - **Common cause**: PDFs are now rendered in the browser with the Forme PDF engine (WebAssembly), so failures usually come from a - blocked download, an extreme browser memory limit, or a custom CSP that blocks WebAssembly (it needs `'wasm-unsafe-eval'` in `script-src`). - **Checks**: confirm - the browser is up to date, the page hasn't been opened in a restricted iframe, and that no extension is intercepting - the download. There is no server-side printer to inspect. - + + Emails are only sent when `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS` and `SMTP_FROM` are all set. Until then they're + written to the app log. If they're set, check `SMTP_PORT` and `SMTP_SECURE` against your provider's settings and + look for SMTP errors in the log. + - - - **Common cause**: storage health failed (not only database). - **Fix**: inspect the endpoint response payload and - check the `storage` field: http://127.0.0.1:3000/api/health - + + Look at which entry says `unhealthy`. For `storage`, check the `/app/data` mount or your S3 settings. For `redis`, + check that Redis is running and `REDIS_URL` is correct. + - - - **Cause**: local upload storage wasn't mounted to a persistent volume. - **Fix**: add a volume mount like - `./data:/app/data` and redeploy. - + + Nothing persistent was mounted at `/app/data`, so uploads lived inside the old container. Mount a volume there as + in the example above. Pictures uploaded before the change are gone. + - - - **Expected behavior**: if SMTP isn't fully configured, the app logs emails to the console. - **Fix**: set - `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM`, then verify `SMTP_PORT` and `SMTP_SECURE`. - + + The app is using virtual-hosted addresses, which put the bucket name in front of the endpoint. MinIO, SeaweedFS + and most self-hosted services need path-style addresses. Set `S3_FORCE_PATH_STYLE="true"`. + - - - **Common cause**: redirect URI is not the app origin or a local loopback callback. - **Fix**: use an app-origin or loopback redirect URI, or enable `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` only on a trusted self-hosted deployment that needs arbitrary redirect URIs. - + + `ENCRYPTION_SECRET` isn't set. Set it to at least 32 characters and recreate the container. A shorter value + stops the app at startup with `Invalid environment variables`. + - - - **Common cause**: The S3 client is using virtual-hosted-style addressing (prepending the bucket name to the endpoint), but your S3-compatible storage expects path-style addressing. - - **Symptom**: Error message like `getaddrinfo ENOTFOUND mybucket.s3-server.com` when your endpoint is `s3-server.com`. - - **Fix**: Set `S3_FORCE_PATH_STYLE="true"` in your environment. This is required for most self-hosted S3-compatible services like MinIO, SeaweedFS, etc. + + The editor renders PDFs in the browser with WebAssembly. If you add your own Content Security Policy at the proxy, + allow `'wasm-unsafe-eval'` in `script-src`. Also check for browser extensions that block downloads. -## Serve a public resume at the instance root +## Related pages -To display one public resume at `/` instead of the marketing home, set the optional server environment variable `ROOT_RESUME_ID` on the application service: - -```yaml -environment: - APP_URL: https://resume.example.com - ROOT_RESUME_ID: your-resume-id -``` - -Find the resume ID in its owner's builder URL: `/builder/`. The resume must already have **Allow Public Access** enabled in Sharing. This setting does not change its visibility. Password protection and the download-button preference still apply, and the ordinary `//` URL continues to work. Renaming the username or slug does not change the configured ID. - -Restart the application after setting or changing `ROOT_RESUME_ID`. With Docker Compose, run `docker compose up -d` to recreate the application with the new environment. Unset the variable or leave it blank, then restart, to restore the marketing home. A missing, deleted, or private target shows an unavailable page, including when its owner visits `/`. - -Keep `APP_URL` set to the public origin and proxy the whole application normally, including API, uploads, fonts, and assets. Root mode uses that configured origin for its canonical URL; it does not infer a domain from request headers. A successful password challenge returns visitors to `/`. - -This is a single-resume setting for one self-hosted instance. It does not register custom domains, manage DNS or TLS, or hide the rest of the application. Login and the dashboard remain available at their usual paths. - -## AI agent run duration - -Each individual agent run has a four-minute execution timeout, reserving up to one minute for saving and cleanup within the shared five-minute budget. This is the same on Docker and Vercel Hobby. Normal answers return as soon as they finish; the limit never applies to an entire conversation. Follow-up questions get a fresh timer. On timeout, completed edits and saved history remain available, and you can ask the agent to continue. - -Multiple application instances should share `REDIS_URL` and `DEPLOYMENT_NAMESPACE` for rate limits, resumable streams, cancellation, resume notifications, and view deduplication. Without Redis, all core resume features work on a single instance. Private agent attachments need S3-compatible storage or private Blob; local disk supports public uploads only. +- [Environment variables](/self-hosting/environment-variables): every setting, with defaults. +- [Deployment examples](/self-hosting/examples): reverse proxies, S3 storage, Redis and local AI. +- [Self-hosting with Kubernetes](/self-hosting/kubernetes): the same setup as Kubernetes manifests. +- [Checking service status](/guides/checking-service-status): the status of the hosted instance. diff --git a/docs/self-hosting/environment-variables.mdx b/docs/self-hosting/environment-variables.mdx new file mode 100644 index 000000000..3fda26c1f --- /dev/null +++ b/docs/self-hosting/environment-variables.mdx @@ -0,0 +1,210 @@ +--- +title: "Environment variables" +description: "Every environment variable a self-hosted Reactive Resume server reads: required settings, database, sign-in, email, storage, AI, Redis and feature flags." +--- + +Reactive Resume is configured entirely through environment variables. This page lists every variable the server reads, +grouped by what it controls. Only three are required: `APP_URL`, `DATABASE_URL` and `AUTH_SECRET`. + +For a working setup, start with [Self-hosting with Docker](/self-hosting/docker) and come back here when you want to +turn on an optional feature. + +## How values are read + +- The server reads variables from its process environment. In a source checkout it also loads a `.env` file from the + workspace root. A variable already set in the environment always wins over the file. +- An empty value counts as unset. `SMTP_HOST=""` is the same as not setting `SMTP_HOST` at all. +- Values are validated at startup. If one is missing or malformed, the server stops and names the variable in its logs. +- Boolean variables accept `true` or `false`. `1`/`0`, `yes`/`no` and `on`/`off` also work; any other value stops the + server. +- Changes take effect after a restart. With Docker Compose, run `docker compose up -d` to recreate the container with + the new environment. + + + PDFs are rendered by the app itself, so there is no printer service to configure. The v4 and early v5 variables + `PRINTER_*`, `BROWSERLESS_*` and `CHROME_*` are no longer read; you can remove them. + + +## Required + +| Variable | Description | +| --- | --- | +| `APP_URL` | The public address people use to reach your instance, for example `https://resume.example.com`. Must start with `http://` or `https://`. Used for sign-in redirects, OAuth callbacks, links in emails, public resume links, social previews and upload URLs. With an `https://` address, session cookies are marked secure. | +| `DATABASE_URL` | PostgreSQL connection string: `postgresql://USER:PASSWORD@HOST:5432/DATABASE`. Must start with `postgres://` or `postgresql://`. URL-encode special characters in the password. Add provider options such as `?sslmode=require` when your database needs them. | +| `AUTH_SECRET` | Secret used to sign sessions and encrypt sign-in data. Generate one with `openssl rand -hex 32`. | + + + Keep `AUTH_SECRET` the same for the life of your instance. Changing it signs everyone out and makes data encrypted with + it unreadable, including two-factor authentication secrets and the keys that sign API and MCP access tokens. + + +## Server + +| Variable | Default | Description | +| --- | --- | --- | +| `PORT` | `3000` | Port the production server listens on. The official image sets it to `3000`. If you change it, update your port mapping and health check to match. | +| `NODE_ENV` | `production` in the image | When `production`, the server listens on `PORT` and turns on rate limiting. Leave it as the image sets it. | +| `SERVER_PORT` | `3001` | Port the server listens on when `NODE_ENV` is not `production`, which is the local development setup. Ignored by the image. | +| `ROOT_RESUME_ID` | unset | Shows one public resume at `/` instead of the home page. See [Show one resume at your root address](/self-hosting/examples#show-one-resume-at-your-root-address). | + +## Database + +| Variable | Default | Description | +| --- | --- | --- | +| `DATABASE_MIGRATION_URL` | `DATABASE_URL` | Separate connection string for the migrations that run at startup. Set it when `DATABASE_URL` goes through a connection pooler (such as PgBouncer or a provider's pooled endpoint) that cannot run migrations. | +| `DATABASE_POOL_MAX` | `10` | Maximum number of database connections each server process opens, from `1` to `100`. | +| `STRICT_SCHEMA_CHECK` | `false` | After migrations, the server compares the live schema with what the migrations expect. By default a mismatch is logged and the server keeps starting. Set `true` to refuse to start instead. | + +## Sign-in + +| Variable | Default | Description | +| --- | --- | --- | +| `BETTER_AUTH_API_KEY` | unset | Connects the instance to the Better Auth dashboard service. Most instances leave it empty. | +| `BETTER_AUTH_INTERNAL_URL` | `http://127.0.0.1:$PORT` | Address the server uses to reach itself when it verifies OAuth access tokens from API and MCP clients. Set it only if the server cannot reach itself on `127.0.0.1` at `PORT`. | + +### Social sign-in + +Each provider appears on the sign-in page once both of its variables are set. See [Single sign-on](/self-hosting/sso) +for callback URLs and provider setup. + +| Variables | Provider | +| --- | --- | +| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | Google | +| `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | GitHub | +| `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` | LinkedIn | + +### Custom OAuth or OpenID Connect provider + +Use these to sign in through your own identity provider, such as Authentik, Keycloak or Authelia. The provider is +turned on when the client ID and secret are set together with either a discovery URL or all three manual endpoints. +Its callback URL is `APP_URL` followed by `/api/auth/callback/custom`. + +| Variable | Default | Description | +| --- | --- | --- | +| `OAUTH_PROVIDER_NAME` | `Custom OAuth` | Name shown on the sign-in button. | +| `OAUTH_CLIENT_ID` | unset | Client ID issued by your provider. | +| `OAUTH_CLIENT_SECRET` | unset | Client secret issued by your provider. | +| `OAUTH_DISCOVERY_URL` | unset | The provider's `.well-known/openid-configuration` address. Preferred for OpenID Connect providers. | +| `OAUTH_AUTHORIZATION_URL` | unset | Authorization endpoint. Use with the next two instead of a discovery URL. | +| `OAUTH_TOKEN_URL` | unset | Token endpoint. | +| `OAUTH_USER_INFO_URL` | unset | User info endpoint. | +| `OAUTH_SCOPES` | `openid profile email` | Space-separated scopes to request. | + +## Email + +Reactive Resume sends email for account verification, password resets and email changes. Sending turns on only when +`SMTP_HOST`, `SMTP_USER`, `SMTP_PASS` and `SMTP_FROM` are all set. Until then, each email is written to the server log +instead, so you can still copy verification links from there. + +| Variable | Default | Description | +| --- | --- | --- | +| `SMTP_HOST` | unset | SMTP server hostname. | +| `SMTP_PORT` | `587` | SMTP server port. | +| `SMTP_USER` | unset | SMTP username. | +| `SMTP_PASS` | unset | SMTP password. | +| `SMTP_FROM` | unset | Sender address, for example `Reactive Resume `. | +| `SMTP_SECURE` | `false` | `true` connects with TLS from the start (usually port `465`). `false` upgrades the connection with STARTTLS when the server offers it (usually port `587`). | + +## Storage + +Uploads such as profile pictures, files attached to applications and Assistant attachments are stored in one of three +backends. + +| Variable | Default | Description | +| --- | --- | --- | +| `STORAGE_BACKEND` | automatic | `local`, `s3` or `blob`. When unset: `s3` if `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` and `S3_BUCKET` are all set; otherwise `blob` on Vercel and `local` everywhere else. | +| `LOCAL_STORAGE_PATH` | `/app/data` in the image | Folder for local uploads. Must be an absolute path. In a source checkout it defaults to `data/` in the repository. The server checks that it is writable at startup and refuses to start if it is not. | +| `S3_ACCESS_KEY_ID` | unset | Access key for S3 or an S3-compatible service. | +| `S3_SECRET_ACCESS_KEY` | unset | Secret key. | +| `S3_BUCKET` | unset | Bucket name. The bucket can stay private: the app reads objects with its own credentials and serves them itself. | +| `S3_REGION` | `us-east-1` | Bucket region. | +| `S3_ENDPOINT` | AWS | Endpoint for non-AWS services, for example `https://.r2.cloudflarestorage.com` or `http://seaweedfs:8333`. | +| `S3_FORCE_PATH_STYLE` | `false` | `true` for path-style addresses (`https://endpoint/bucket`), which MinIO and SeaweedFS need. `false` for virtual-hosted addresses (`https://bucket.endpoint`), used by AWS S3 and Cloudflare R2. | +| `BLOB_READ_WRITE_TOKEN` | unset | Vercel Blob token. On Vercel it is injected when you connect a Blob store. | +| `BLOB_STORE_ID` | unset | Vercel Blob store ID, when the token alone does not identify the store. | +| `DEPLOYMENT_NAMESPACE` | `default` | Prefix for Redis keys and Blob paths, so several instances can share one Redis or Blob store without mixing data. Letters, numbers, `.`, `_` and `-` only. On Vercel it is `production`, or the branch address on previews. | + + + Local storage keeps only public uploads. Files people attach in the Assistant are private and need S3-compatible + storage or Vercel Blob; on local storage, attaching a file fails. + + +Switching backends does not move files that are already stored. Copy them yourself before you switch. + +## AI and Redis + +AI features are off until `ENCRYPTION_SECRET` is set. People then add their own provider and key in +**Settings → AI & developer**; see [Connecting an AI provider](/guides/using-ai). + +| Variable | Default | Description | +| --- | --- | --- | +| `ENCRYPTION_SECRET` | unset | Encrypts the AI provider keys people save. At least 32 characters; generate one with `openssl rand -hex 32` and keep it different from `AUTH_SECRET`. A shorter value stops the server at startup. Without it, adding a provider fails and the Assistant says it isn't set up on this server. | +| `REDIS_URL` | unset | Redis connection string, `redis://` or `rediss://`. Optional. | +| `AI_TEST_TIMEOUT_MS` | `30000` | How long **Save and test** waits for a provider to answer, in milliseconds. Raise it for local models that load slowly on first use. | + + + Changing `ENCRYPTION_SECRET` makes every saved provider key unreadable. People then need to enter their keys again. + + +Redis is optional on a single server. Without it, everything works, with these limits: + +- An Assistant reply that is interrupted by a page reload can't be picked up again. +- Rate limits, **Stop** on a running Assistant reply, live updates between open tabs and the check that counts each + public resume view once an hour are kept in the memory of one server process. + +Set `REDIS_URL` when you run more than one server process, or when you want replies to survive a reload. When Redis is +configured, the [health endpoint](/self-hosting/docker#check-the-health-endpoint) also checks it. + +## Feature flags + +All flags default to `false`. + +| Variable | When set to `true` | +| --- | --- | +| `FLAG_DISABLE_SIGNUPS` | No new accounts can be created, by email or by social sign-in. Existing accounts keep working. Create your own account first. | +| `FLAG_DISABLE_EMAIL_AUTH` | Turns off email and password sign-in and sign-up, along with forgot password and reset password. People sign in with social or custom OAuth providers only, so configure at least one first. | +| `FLAG_DISABLE_IMAGE_PROCESSING` | Stores uploaded pictures as they are. Normally pictures are resized to fit 800 × 800 pixels and saved as JPEG. Useful on low-powered hardware such as a Raspberry Pi. | +| `FLAG_DISABLE_API_RATE_LIMIT` | Turns off rate limiting on sign-in, sign-up, the OAuth endpoints and requests made with API keys. Other limits, such as PDF export and AI requests, stay on. Intended for test installations. | +| `FLAG_ALLOW_UNSAFE_AI_BASE_URL` | Lets AI providers use `http://` addresses and private or local network addresses, such as an Ollama server on your network. Without it, a provider's base URL must be public `https://`. | +| `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` | Lets MCP and OAuth clients that register themselves use any redirect address, including custom schemes and private hosts. By default a redirect must go to your instance's own address, an `http://` loopback address (`localhost`, `127.0.0.1`, `::1`) or a public `https://` address. | + + + Only turn on the two `FLAG_ALLOW_UNSAFE_*` flags on an instance where you trust every user. On a shared instance, an + unsafe AI base URL lets users make your server call internal network addresses, and an unsafe redirect URI can be used + for phishing or to steal access tokens. + + +## Deployment aliases + +On Vercel (when `VERCEL=1`), the server fills in some variables from the ones Vercel's integrations inject. A value +you set yourself always wins. + +| Variable | Filled from | +| --- | --- | +| `APP_URL` | `https://` plus `VERCEL_PROJECT_PRODUCTION_URL` in production, or `VERCEL_URL` on previews | +| `DATABASE_URL` | `POSTGRES_URL` | +| `DATABASE_MIGRATION_URL` | `DATABASE_URL_UNPOOLED`, then `POSTGRES_URL_NON_POOLING` | +| `REDIS_URL` | `KV_URL` | +| `STORAGE_BACKEND` | `blob`, unless S3 credentials are set | +| `DEPLOYMENT_NAMESPACE` | `production`, or `VERCEL_BRANCH_URL` on previews | + +Vercel builds also read `ALLOW_PREVIEW_MIGRATIONS`: preview builds refuse to run migrations unless it is `true`, so a +preview can't change your production database by accident. See [Self-hosting on Vercel](/self-hosting/vercel). + +## Development and tooling only + +These appear in `.env.example` but are not used by a running server: + +| Variable | Used by | +| --- | --- | +| `GOOGLE_CLOUD_API_KEY` | The script that regenerates the font list. | +| `CROWDIN_PROJECT_ID`, `CROWDIN_API_TOKEN` | Translation sync tooling. | +| `COVER_LETTER_TEST_DATABASE_URL`, `OAUTH_TEST_DATABASE_URL` | Test suites that need their own database. | + +See [Development setup](/contributing/development) for working on the code. + +## Related pages + +- [Self-hosting with Docker](/self-hosting/docker): a complete setup with PostgreSQL. +- [Deployment examples](/self-hosting/examples): reverse proxies, S3 storage, Redis and local AI. +- [Single sign-on](/self-hosting/sso): provider setup for the sign-in variables. diff --git a/docs/self-hosting/examples.mdx b/docs/self-hosting/examples.mdx index 98042aa6a..1e0132ba0 100644 --- a/docs/self-hosting/examples.mdx +++ b/docs/self-hosting/examples.mdx @@ -1,174 +1,55 @@ --- -title: "Docker Compose examples" -description: "Ready-to-use Docker Compose examples for self-hosting Reactive Resume with Postgres, Traefik, Caddy, Nginx Proxy Manager, and other common deployment stacks." +title: "Deployment examples" +description: "Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more." --- - - **From v5.1.0 onwards** — PDF generation now runs entirely client-side with the Forme PDF engine (WebAssembly). None of the examples below require a Browserless or Chromium service. Older configurations that still define a `printer` service or set `BROWSERLESS_TOKEN` / `PRINTER_*` will continue to start, but those services are inert and can be removed. - +Each section on this page solves one common self-hosting task and builds on the two-container setup from +[Self-hosting with Docker](/self-hosting/docker). Replace `resume.example.com` with your own domain, and keep the +`.env` file from that guide unless a section says otherwise. -## Overview +## What every reverse proxy needs -Self-hosted setups vary. You might run on a single VPS or a Kubernetes cluster, behind Cloudflare Tunnel, or behind a reverse proxy like Traefik or nginx. This page collects Docker Compose configurations for those cases. +Whatever proxy you use, configure it so that: -They go further than the basic setup in the [Self-hosting with Docker](/self-hosting/docker) guide, with reverse proxies, SSL termination, and other common patterns. +- **The whole site goes to the app.** The app serves pages, the API (`/api/`), the MCP server (`/mcp`), uploads and + assets from one address. Don't split or rewrite paths. +- **`APP_URL` is the public address**, for example `APP_URL="https://resume.example.com"`. +- **The client's IP address is passed on, and clients can't fake it.** Rate limits (sign-in attempts, public resume + passwords) are counted per IP address. The app uses the first of these headers it finds, as sent: `CF-Connecting-IP`, + `CF-Connecting-IPv6`, `True-Client-IP`, `X-Forwarded-For` (its first entry), then `X-Real-IP`. Set + `X-Forwarded-For` to the address the proxy sees, replacing any value the client sent, and remove the other three + headers unless a CDN in front of your proxy sets them. The examples below do both. +- **Streaming responses aren't buffered**, and idle reads are allowed for at least 5 minutes. Assistant replies and + live updates stream from the server, and a single Assistant reply can take up to 4 minutes. +- **Request bodies of at least 50 MB are accepted.** People can attach files of up to 25 MB to the Assistant, and + the browser sends them base64-encoded, which makes them about a third larger. - - **Share your setup.** If you have a working configuration that isn't covered here, I'd love to include it. [Open a - pull request](https://github.com/reactive-resume/reactive-resume) with your example added to this page. - + + Once a proxy is in front, don't publish the app's port `3000` on a public + interface, or clients can bypass the proxy and fake their IP address to get around rate limits. Remove the `ports` + entry, or bind it to `127.0.0.1:3000:3000`. + ---- +## Caddy -## Reuse an existing PostgreSQL service +[Caddy](https://caddyserver.com/) gets and renews HTTPS certificates on its own and streams responses without extra +settings, so it needs the least configuration. -Reactive Resume always uses a separate PostgreSQL service; do not embed another database server in the app container. -If your homelab or hosting platform already manages PostgreSQL, reuse it by setting `DATABASE_URL` to that service and -allowing the app container to reach it over the intended private network. Keep database credentials private and do not -expose PostgreSQL to the public internet. - -When the database connection crosses a host or network boundary, require TLS with certificate and hostname verification. -Use `sslmode=verify-full` in `DATABASE_URL` where the provider supports it, or the provider's equivalent verified-TLS -configuration. Private routing limits exposure, but does not itself verify the database server's identity. - -For generic Unraid and homelab container fields, including the `localhost` networking warning, see -[Unraid and other homelab platforms](/self-hosting/docker#unraid-and-other-homelab-platforms). The core resume workflow -does not require Redis or S3. Redis is a separate optional dependency for the AI Agent workspace, while S3-compatible -storage is optional unless you need features that require private object storage; see the -[environment variable reference](/self-hosting/docker#environment-variables) for those boundaries. - ---- - -## Docker with Traefik - -This example uses [Traefik](https://traefik.io/) as a reverse proxy with automatic SSL certificate management via Let's Encrypt. Postgres stays on an internal network. The Traefik dashboard is also routed, at `traefik.${DOMAIN}` behind basic auth — drop those labels if you do not want it reachable. - - - Traefik discovers services through Docker labels and handles SSL certificates, so it needs very little configuration. - - -```yaml compose-traefik.yml lines expandable +```yaml compose.yml services: - traefik: - image: traefik:v3.2 - restart: unless-stopped - command: - - "--api.dashboard=true" - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--entrypoints.web.address=:80" - - "--entrypoints.websecure.address=:443" - - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true" - - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" - - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}" - - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - - "--entrypoints.web.http.redirections.entryPoint.scheme=https" - ports: - - "80:80" - - "443:443" - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - traefik_letsencrypt:/letsencrypt - networks: - - reactive_resume_network - labels: - - "traefik.enable=true" - # Dashboard (optional, remove if not needed) - - "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)" - - "traefik.http.routers.traefik.entrypoints=websecure" - - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" - - "traefik.http.routers.traefik.service=api@internal" - - "traefik.http.routers.traefik.middlewares=auth" - - "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_DASHBOARD_AUTH}" - - postgres: - image: postgres:latest - restart: unless-stopped - environment: - - POSTGRES_DB=postgres - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - volumes: - - postgres_data:/var/lib/postgresql - networks: - - reactive_resume_network - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] - interval: 10s - timeout: 5s - retries: 5 - - reactive_resume: - image: amruthpillai/reactive-resume:latest - restart: unless-stopped - environment: - - APP_URL=https://resume.${DOMAIN} - - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres - - AUTH_SECRET=${AUTH_SECRET} - # Add other optional env vars as needed (SMTP, S3, OAuth, etc.) - volumes: - - reactive_resume_data:/app/data - networks: - - reactive_resume_network - depends_on: - postgres: - condition: service_healthy - labels: - - "traefik.enable=true" - - "traefik.http.routers.reactive-resume.rule=Host(`resume.${DOMAIN}`)" - - "traefik.http.routers.reactive-resume.entrypoints=websecure" - - "traefik.http.routers.reactive-resume.tls.certresolver=letsencrypt" - - "traefik.http.services.reactive-resume.loadbalancer.server.port=3000" - healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then((r) => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1));"] - interval: 30s - timeout: 10s - retries: 3 - -networks: - reactive_resume_network: - driver: bridge - -volumes: - traefik_letsencrypt: - postgres_data: - reactive_resume_data: -``` - -**Environment variables (`.env`):** - -```bash .env -DOMAIN="example.com" -ACME_EMAIL="admin@example.com" -POSTGRES_PASSWORD="your-secure-postgres-password" -AUTH_SECRET="your-auth-secret-from-openssl-rand-hex-32" -# Optional: Traefik dashboard auth (generate with: htpasswd -nb admin password) -TRAEFIK_DASHBOARD_AUTH="admin:$$apr1$$..." -``` - ---- - -## Docker with nginx - -This example uses [nginx](https://nginx.org/) as a reverse proxy with SSL certificates (you'll need to provide your own certificates or use certbot separately). - -```yaml compose-nginx.yml lines expandable -services: - nginx: - image: nginx:alpine + caddy: + image: caddy:2 restart: unless-stopped ports: - "80:80" - "443:443" + - "443:443/udp" volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - - ./certs:/etc/nginx/certs:ro - networks: - - reactive_resume_network + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data postgres: - image: postgres:latest + image: postgres:18 restart: unless-stopped environment: POSTGRES_DB: postgres @@ -176,294 +57,414 @@ services: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql - networks: - - reactive_resume_network healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] interval: 10s timeout: 5s - retries: 5 + retries: 10 - reactive_resume: + reactive-resume: image: amruthpillai/reactive-resume:latest restart: unless-stopped - environment: - - APP_URL=https://resume.${DOMAIN} - - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres - - AUTH_SECRET=${AUTH_SECRET} - # Add other optional env vars as needed (SMTP, S3, OAuth, etc.) + env_file: .env volumes: - reactive_resume_data:/app/data - networks: - - reactive_resume_network depends_on: postgres: condition: service_healthy - healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then((r) => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1));"] - interval: 30s - timeout: 10s - retries: 3 - -networks: - reactive_resume_network: - driver: bridge volumes: + caddy_data: postgres_data: reactive_resume_data: ``` -**nginx configuration (`nginx.conf`):** - -```nginx nginx.conf lines expandable -events { - worker_connections 1024; -} - -http { - upstream reactive_resume { - server reactive_resume:3000; - } - - # Redirect HTTP to HTTPS - server { - listen 80; - server_name _; - return 301 https://$host$request_uri; - } - - # HTTPS server - server { - listen 443 ssl http2; - server_name resume.example.com; - - ssl_certificate /etc/nginx/certs/fullchain.pem; - ssl_certificate_key /etc/nginx/certs/privkey.pem; - - # SSL configuration - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers on; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 10m; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - - # Proxy settings - location / { - proxy_pass http://reactive_resume; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - - # Reasonable timeouts for app requests - proxy_connect_timeout 60s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - # Increase max body size for resume uploads - client_max_body_size 10M; - } +```text Caddyfile +resume.example.com { + request_body { + max_size 50MB + } + reverse_proxy reactive-resume:3000 { + header_up -CF-Connecting-IP + header_up -CF-Connecting-IPv6 + header_up -True-Client-IP + } } ``` - - For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx` - plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion). - +Point your domain's DNS at the server, set `APP_URL="https://resume.example.com"` in `.env`, and run +`docker compose up -d`. Caddy replaces any `X-Forwarded-For` header a client sends with the real client address, and +the `header_up` lines drop the other IP headers. ---- +## Traefik -## Docker Swarm +[Traefik](https://traefik.io/) reads its routes from Docker labels and gets certificates from Let's Encrypt. -This example is a Docker Swarm deployment with health checks, rolling updates, and Traefik integration. It includes SeaweedFS for S3-compatible storage and a PostgreSQL database with custom configuration. - - - Docker Swarm suits multi-node deployments that need high availability and simple scaling. Every service here starts - at one replica; raise `deploy.replicas` on `reactive_resume` once you have more than one node. - - -```yaml compose-swarm.yml lines expandable +```yaml compose.yml lines expandable services: - postgres: - image: postgres:latest - networks: - - reactive_resume_network + traefik: + image: traefik:v3 + restart: unless-stopped + command: + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true" + - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" + - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}" + - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" volumes: - - reactive_resume_postgres_data:/var/lib/postgresql + - /var/run/docker.sock:/var/run/docker.sock:ro + - traefik_letsencrypt:/letsencrypt + + postgres: + image: postgres:18 + restart: unless-stopped environment: - - POSTGRES_DB=$POSTGRES_DB - - POSTGRES_USER=$POSTGRES_USER - - POSTGRES_PASSWORD=$POSTGRES_PASSWORD + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql healthcheck: - test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"] + test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] interval: 10s timeout: 5s - retries: 5 - start_period: 30s - deploy: - mode: replicated - replicas: 1 + retries: 10 - seaweedfs: - image: chrislusf/seaweedfs:latest - command: server -s3 -filer -dir=/data -ip=0.0.0.0 - networks: - - reactive_resume_network - volumes: - - reactive_resume_seaweedfs_data:/data - environment: - - AWS_ACCESS_KEY_ID=$S3_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY - healthcheck: - test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8888"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - deploy: - mode: replicated - replicas: 1 - - seaweedfs_create_bucket: - image: amazon/aws-cli:latest - environment: - - AWS_ACCESS_KEY_ID=$S3_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY - - AWS_DEFAULT_REGION=us-east-1 - entrypoint: > - /bin/sh -c " - until aws --endpoint-url http://seaweedfs:8333 s3api head-bucket --bucket $S3_BUCKET 2>/dev/null || - aws --endpoint-url http://seaweedfs:8333 s3 mb s3://$S3_BUCKET; do - echo 'Waiting for SeaweedFS...'; - sleep 2; - done; - " - networks: - - reactive_resume_network - deploy: - mode: replicated - replicas: 1 - - reactive_resume: - image: ghcr.io/reactive-resume/reactive-resume:latest - networks: - - traefik_network - - reactive_resume_network + reactive-resume: + image: amruthpillai/reactive-resume:latest + restart: unless-stopped + env_file: .env volumes: - reactive_resume_data:/app/data - environment: - - APP_URL=$APP_URL - - DATABASE_URL=$DATABASE_URL - - AUTH_SECRET=$AUTH_SECRET - - GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID - - GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET - - GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID - - GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET - - LINKEDIN_CLIENT_ID=$LINKEDIN_CLIENT_ID - - LINKEDIN_CLIENT_SECRET=$LINKEDIN_CLIENT_SECRET - - SMTP_HOST=$SMTP_HOST - - SMTP_PORT=$SMTP_PORT - - SMTP_USER=$SMTP_USER - - SMTP_PASS=$SMTP_PASS - - SMTP_FROM=$SMTP_FROM - - SMTP_SECURE=$SMTP_SECURE - - S3_ACCESS_KEY_ID=$S3_ACCESS_KEY_ID - - S3_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY - - S3_REGION=$S3_REGION - - S3_ENDPOINT=$S3_ENDPOINT - - S3_BUCKET=$S3_BUCKET - healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then((r) => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1));"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - deploy: - mode: replicated - replicas: 1 - labels: - - "traefik.enable=true" - - "traefik.http.routers.app.rule=Host(`rxresu.me`)" - - "traefik.http.routers.app.entrypoints=websecure" - - "traefik.http.routers.app.tls=true" - - "traefik.http.services.app.loadbalancer.server.port=3000" - -configs: - reactive_resume_postgres_config: - name: reactive_resume_postgres_config - external: true - -networks: - traefik_network: - external: true - reactive_resume_network: - name: reactive_resume_network - driver: overlay - attachable: true + depends_on: + postgres: + condition: service_healthy + labels: + - "traefik.enable=true" + - "traefik.http.routers.reactive-resume.rule=Host(`resume.example.com`)" + - "traefik.http.routers.reactive-resume.entrypoints=websecure" + - "traefik.http.routers.reactive-resume.tls.certresolver=letsencrypt" + - "traefik.http.services.reactive-resume.loadbalancer.server.port=3000" + # Drop client-supplied IP headers; Traefik sets X-Forwarded-For itself. + - "traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.CF-Connecting-IP=" + - "traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.CF-Connecting-IPv6=" + - "traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.True-Client-IP=" + - "traefik.http.routers.reactive-resume.middlewares=reactive-resume-ip" volumes: - reactive_resume_postgres_data: - name: reactive_resume_postgres_data - reactive_resume_seaweedfs_data: - name: reactive_resume_seaweedfs_data + traefik_letsencrypt: + postgres_data: reactive_resume_data: - name: reactive_resume_data ``` -**Deploy the stack:** +Add `ACME_EMAIL="you@example.com"` to `.env` for Let's Encrypt notices. Traefik skips containers whose health check +fails, so it only routes to the app once `/api/health` answers `200`. -```bash -docker stack deploy -c compose-swarm.yml reactive_resume +## nginx + +With [nginx](https://nginx.org/) you manage certificates yourself, for example with +[Certbot](https://certbot.eff.org/). Add this service to the compose file from the Docker guide, and remove the +`ports` entry from the `reactive-resume` service: + +```yaml compose.yml + nginx: + image: nginx:stable-alpine + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./certs:/etc/nginx/certs:ro + depends_on: + - reactive-resume ``` -**Useful commands:** +```nginx nginx.conf lines expandable +server { + listen 80; + server_name resume.example.com; + return 301 https://$host$request_uri; +} -```bash -# Check service status -docker stack services reactive_resume +server { + listen 443 ssl; + http2 on; + server_name resume.example.com; -# View logs for the app -docker service logs -f reactive_resume_reactive_resume + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; -# Scale the app -docker service scale reactive_resume_reactive_resume=3 + # Assistant attachments can be up to 25 MB, sent base64-encoded. + client_max_body_size 50m; -# Remove the stack -docker stack rm reactive_resume + location / { + proxy_pass http://reactive-resume:3000; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + + # Don't pass client-supplied IP headers on (an empty value removes them). + proxy_set_header CF-Connecting-IP ""; + proxy_set_header CF-Connecting-IPv6 ""; + proxy_set_header True-Client-IP ""; + + # Assistant replies and live updates stream; don't hold them back. + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} ``` +## Use an existing PostgreSQL server + +If you already run PostgreSQL, or use a managed database, leave out the `postgres` service and point `DATABASE_URL` +at your server. Create an empty database and a user that owns it; the app creates its tables on first start. + +```bash .env +DATABASE_URL="postgresql://reactive_resume:password@db.example.com:5432/reactive_resume?sslmode=verify-full" +``` + +- Use `sslmode=verify-full` (or your provider's equivalent) whenever the connection leaves the host, so the app checks + the server's certificate. +- If `DATABASE_URL` points at a connection pooler, add a direct connection in `DATABASE_MIGRATION_URL` for startup + migrations. +- Never expose PostgreSQL to the internet without TLS and a firewall. + +## Store uploads in S3-compatible storage + +S3 storage lets you drop the `/app/data` volume, and it's required if people attach files in the Assistant. The app +switches to S3 once the access key, secret key and bucket are all set. The bucket can stay private: the app reads +objects with its own credentials and serves them itself. + + + + ```bash .env + S3_ACCESS_KEY_ID="AKIA..." + S3_SECRET_ACCESS_KEY="..." + S3_BUCKET="my-reactive-resume" + S3_REGION="eu-central-1" + ``` + + + ```bash .env + S3_ACCESS_KEY_ID="..." + S3_SECRET_ACCESS_KEY="..." + S3_BUCKET="reactive-resume" + S3_REGION="auto" + S3_ENDPOINT="https://.r2.cloudflarestorage.com" + ``` + + + ```bash .env + S3_ACCESS_KEY_ID="..." + S3_SECRET_ACCESS_KEY="..." + S3_BUCKET="reactive-resume" + S3_ENDPOINT="http://minio:9000" + S3_FORCE_PATH_STYLE="true" + ``` + + + +The bucket must exist before the app starts. Check `/api/health`: its `storage` entry should show `"type": "s3"` and +`"status": "healthy"`. Files already in `/app/data` aren't moved; copy them into the bucket, keeping their paths, if you +switch an existing instance. + +## Add Redis and turn on AI + +AI features need `ENCRYPTION_SECRET`. Redis is optional on a single server, but with it an Assistant reply keeps +streaming after a page reload. Add a Redis service to your compose file: + +```yaml compose.yml + redis: + image: redis:8 + restart: unless-stopped + command: redis-server --appendonly yes + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 +``` + +Add `redis_data:` under `volumes:`, and add `redis` to the app's `depends_on` with `condition: service_healthy`. Then +add to `.env`: + +```bash .env +# Generate with: openssl rand -hex 32. Keep it different from AUTH_SECRET. +ENCRYPTION_SECRET="..." +REDIS_URL="redis://redis:6379" +``` + +Run `docker compose up -d`. People can now add their own AI provider; see [Connecting an AI provider](/guides/using-ai) +and [Using the Assistant](/guides/using-the-assistant). + - This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and - labels based on your Traefik configuration. + Redis holds streaming Assistant replies, which include people's messages. Keep it on the private Docker network and + don't publish its port. ---- +## Use a local AI model with Ollama -## Contributing your setup +To keep AI requests on your own hardware, run [Ollama](https://ollama.com/) next to the app. By default the app only +accepts public `https://` provider addresses, so you turn on a flag that allows local ones. -Have a different deployment setup that works well? Consider contributing it here. Some examples: + + `FLAG_ALLOW_UNSAFE_AI_BASE_URL` lets every user make your server send requests to any address on your network. Only + turn it on for an instance where you trust every user, such as a personal or family server. + -- Kubernetes / Helm charts -- Cloudflare Tunnel -- Caddy reverse proxy -- Docker with Portainer -- Podman configurations -- Cloud-specific deployments (AWS ECS, Google Cloud Run, Azure Container Apps) + + + ```yaml compose.yml + ollama: + image: ollama/ollama + restart: unless-stopped + volumes: + - ollama_data:/root/.ollama + ``` -To contribute, [open a pull request](https://github.com/reactive-resume/reactive-resume) with your example added to this page. Include: + Add `ollama_data:` under `volumes:`, start it, and download a model: -1. A brief description of when/why someone would use this setup -2. The complete Docker Compose (or equivalent) configuration -3. Any additional configuration files (nginx.conf, etc.) -4. Required environment variables + ```bash + docker compose up -d ollama + docker compose exec ollama ollama pull llama3.1 + ``` + + + + Add to `.env`, next to the `ENCRYPTION_SECRET` from the previous section: + + ```bash .env + FLAG_ALLOW_UNSAFE_AI_BASE_URL="true" + # Local models can take a while to load on first use. + AI_TEST_TIMEOUT_MS="120000" + ``` + + Run `docker compose up -d` to apply it. + + + + Open **Settings**, then **AI & developer**, and select **Add provider**. Choose **Ollama Cloud** as the provider and + fill in: + + - **API key**: any text, for example `ollama`. A local Ollama server ignores it. + - **Model**: the model you pulled, for example `llama3.1`. + - **Base URL**: `http://ollama:11434/api` + + Select **Save and test**. The app saves the provider once Ollama answers. + + + Add provider dialog with Ollama Cloud selected, model llama3.1, name Home server Ollama and base URL http://ollama:11434/api + + + + +Any server with an OpenAI-compatible API works the same way: choose **OpenAI-compatible** and enter its address, such +as `http://llama-server:8080/v1`. + +## Run a private instance + +For a personal or team server that nobody else can join: + +1. Create your own account (and your team's) first. +2. Add `FLAG_DISABLE_SIGNUPS="true"` to `.env` and run `docker compose up -d`. + +New sign-ups are then refused, by email and by social sign-in. People who already have accounts sign in as usual. + +To allow sign-in only through your company's identity provider, set up a custom OAuth provider (see +[Single sign-on](/self-hosting/sso)), then add `FLAG_DISABLE_EMAIL_AUTH="true"`. This removes email and password +sign-in, along with password resets. + +## Show one resume at your root address + +To use your instance as a personal resume site, set `ROOT_RESUME_ID`. Visitors to `/` then see that resume's public +page instead of the home page. + + + + In the editor, select **Share**, and on the **Link** tab turn on **Public link**. See + [Sharing your resume publicly](/guides/sharing-your-resume-publicly). + + + + The ID is the last part of the editor's address: `https://resume.example.com/builder/`. + + + + ```bash .env + ROOT_RESUME_ID="" + ``` + + Run `docker compose up -d`. + + + +- The resume's own settings still apply: a password still protects it, and **Visitors can download the PDF** still + controls the download button. Its usual `//` address keeps working. +- Renaming the address or username doesn't break it; the ID stays the same. +- If the resume is deleted or its public link is turned off, `/` shows an unavailable page, even to you. +- Sign-in and your documents stay at their usual addresses. The root page asks search engines not to index it. + +Remove the variable, or leave it empty, and restart to bring back the home page. + +## Run several app containers + +To run more than one app container, for high availability or Docker Swarm, every container must share state: + +- **The same database, `AUTH_SECRET` and `ENCRYPTION_SECRET`.** +- **Redis**, set in `REDIS_URL`, so rate limits, stopping an Assistant reply and live updates work across containers. +- **S3-compatible storage** instead of `/app/data`, so every container sees the same uploads. +- **The same `DEPLOYMENT_NAMESPACE`** (the default, `default`, is fine), or leave it unset everywhere. + +Migrations are safe to start in parallel: containers wait for each other, and only one applies changes. A Docker +Swarm service then looks like this: + +```yaml compose-swarm.yml +services: + reactive-resume: + image: amruthpillai/reactive-resume:v6 + env_file: .env + networks: + - reactive_resume + deploy: + replicas: 2 + update_config: + order: start-first + failure_action: rollback + +networks: + reactive_resume: + driver: overlay +``` + +Deploy it with `docker stack deploy -c compose-swarm.yml reactive-resume`, next to your PostgreSQL, Redis and S3 +services, and route traffic to it with your proxy. + +## Share your setup + +Have a working setup that isn't covered here, such as Podman, Portainer or Cloudflare Tunnel? +[Open a pull request](https://github.com/reactive-resume/reactive-resume) that adds it to this page. Include when +someone would use it, the complete configuration, and the environment variables it needs. + +## Related pages + +- [Environment variables](/self-hosting/environment-variables): every setting, with defaults. +- [Self-hosting with Kubernetes](/self-hosting/kubernetes): the same setup as Kubernetes manifests. +- [Single sign-on](/self-hosting/sso): Google, GitHub, LinkedIn and custom OAuth providers. diff --git a/docs/self-hosting/kubernetes.mdx b/docs/self-hosting/kubernetes.mdx index ab949786f..be9f88206 100644 --- a/docs/self-hosting/kubernetes.mdx +++ b/docs/self-hosting/kubernetes.mdx @@ -1,52 +1,28 @@ --- title: "Self-hosting with Kubernetes" -description: "How to self-host Reactive Resume on Kubernetes with plain manifests: PostgreSQL, persistent uploads, Secrets, ingress and verification steps." +description: "Deploy Reactive Resume on Kubernetes with plain manifests: PostgreSQL, a Secret, persistent uploads, health probes, Ingress, scaling and updates." --- - - **From v5.1.0 onwards** — the builder generates PDFs in the browser with the Forme PDF engine (WebAssembly). New deployments no - longer require Browserless, Chromium, or any external print service as a dependency. The `PRINTER_*` and - `BROWSERLESS_*` environment variables are no longer read and can be removed from your configuration. - +This guide deploys Reactive Resume to a Kubernetes cluster with plain manifests. The result matches the +[Docker setup](/self-hosting/docker): one app Deployment serving everything on port `3000`, a separate PostgreSQL +database, and persistent storage for uploads. Adapt the storage class and Ingress settings to your cluster. -## Overview +## Before you start -Reactive Resume runs on Kubernetes as a single Deployment that serves both the web app and the API on port `3000`, the -same way the official Docker image does. The rest of the stack matches the [Self-hosting with Docker](/self-hosting/docker) -guide: +You need: -- **PostgreSQL** must run as a separate service. The app connects to it through `DATABASE_URL`; no all-in-one image with - an embedded database is planned. -- **Persistent storage** for uploads. Without S3, uploads live under `/app/data`, so a PersistentVolumeClaim must be - mounted there. -- **Secrets** for `APP_URL`, `DATABASE_URL`, and `AUTH_SECRET`. Optional features (SMTP, S3, OAuth, AI) use the same - environment variables as the Docker guide's [environment variable reference](/self-hosting/docker#environment-variables). +- A cluster with `kubectl` access and a default StorageClass for PersistentVolumeClaims. +- An Ingress controller and a way to issue TLS certificates, such as cert-manager. +- A DNS name for your instance, for example `resume.example.com`. +- About 250m CPU and 512 MiB of memory for the app Pod, plus room for PostgreSQL if it runs in the cluster. -Everything below uses plain Kubernetes manifests for a Linux cluster. Adapt the storage and Ingress settings to your -cluster. A community Helm chart is linked at the end of the page; it is maintained outside this repository. +The image is `amruthpillai/reactive-resume` on Docker Hub or `ghcr.io/reactive-resume/reactive-resume` on GitHub +Container Registry, built for `amd64` and `arm64`. The examples pin the `v6` tag, which gets fixes and new features +without moving to the next major version. - - - Use ghcr.io/reactive-resume/reactive-resume:latest or amruthpillai/reactive-resume:latest. - - Stores accounts, resumes, and application data. Runs separately, never embedded in the app image. - +## Create the namespace and Secret -## Minimum requirements - - - - A running cluster with kubectl access and a default StorageClass for PersistentVolumeClaims. - - - An Ingress controller (nginx, Traefik, …) and a way to issue TLS certificates, for example cert-manager. - - 1 vCPU / 1 GB RAM minimum for the app Pod (2 GB recommended when PostgreSQL runs in the same cluster). - - -## Create the namespace - -Save this as `namespace.yaml`. Apply it before any of the namespaced resources below. +Save both manifests. The Secret holds every setting and is passed to the app as environment variables. ```yaml namespace.yaml apiVersion: v1 @@ -55,11 +31,6 @@ metadata: name: reactive-resume ``` -## Required Secrets - -Configuration is passed to the Pod as environment variables. Store the values in a Secret and reference it from the -Deployment with `envFrom`: - ```yaml secret.yaml apiVersion: v1 kind: Secret @@ -68,66 +39,42 @@ metadata: namespace: reactive-resume type: Opaque stringData: - # Canonical public URL of your instance. Must match the HTTPS URL users actually visit. + # The public HTTPS address people use. Must match the Ingress host. APP_URL: "https://resume.example.com" - # "postgres" is the Service name from the PostgreSQL section below. + # "postgres" is the Service name from postgres.yaml. DATABASE_URL: "postgresql://postgres:REPLACE_WITH_DATABASE_PASSWORD@postgres:5432/postgres" - # Used by the example PostgreSQL Deployment. Must match the password in DATABASE_URL. + # Read by the example PostgreSQL Deployment. Same password as in DATABASE_URL. POSTGRES_PASSWORD: "REPLACE_WITH_DATABASE_PASSWORD" - # Generate with: openssl rand -hex 32 - AUTH_SECRET: "REPLACE_WITH_A_RANDOM_64_CHAR_HEX_STRING" - - # --- Optional (see the Docker guide's environment variable reference) --- - # SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM, SMTP_SECURE - # S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_REGION, S3_ENDPOINT, S3_BUCKET, S3_FORCE_PATH_STYLE - # ENCRYPTION_SECRET, REDIS_URL (AI features) - # FLAG_DISABLE_SIGNUPS, FLAG_DISABLE_EMAIL_AUTH (feature flags) + # openssl rand -hex 32 + AUTH_SECRET: "REPLACE_WITH_A_RANDOM_SECRET" + # Optional: turns on AI features. openssl rand -hex 32, different from AUTH_SECRET. + # ENCRYPTION_SECRET: "" + # Any other variable from the environment variable reference goes here too. ``` - - - Generate a strong secret and paste it into `AUTH_SECRET`. - - ```bash - openssl rand -hex 32 - ``` - - - - - Set `APP_URL` to the public HTTPS URL you will reach through the Ingress. If it does not match the URL you actually - use, sign-in redirects and cookies will misbehave. - - - - Point `DATABASE_URL` at your PostgreSQL instance. Inside the cluster the host is the Service DNS name (for example - `postgres` in the same namespace) — never `localhost`, which resolves to the app Pod itself. - For the PostgreSQL example below, generate a separate password with `openssl rand -hex 32` and use it in both - `POSTGRES_PASSWORD` and `DATABASE_URL`. URL-encode special characters in connection-string passwords. - - +Generate the secrets and the database password with `openssl rand -hex 32`, and paste them in. See +[Environment variables](/self-hosting/environment-variables) for everything else you can add, such as SMTP, S3 and +single sign-on. - `stringData` keeps the example readable. Base64-encoded `data` is not encryption. Keep files containing real secrets - out of version control; for GitOps, use encrypted Secrets or an External Secrets mapping. Retain `AUTH_SECRET` - across Pod restarts and upgrades. + `stringData` keeps the example readable, but a Secret is only base64-encoded, not encrypted. Keep this file out of + version control. For GitOps, use Sealed Secrets, SOPS or External Secrets. Never change `AUTH_SECRET` or + `ENCRYPTION_SECRET` once people use the instance. -## PostgreSQL dependency +## Run PostgreSQL -PostgreSQL is the only required service next to the app. You can use a managed database outside the cluster, an operator -such as CloudNativePG, or a chart such as the HelmForge or Bitnami PostgreSQL charts. The minimal example below is -enough for a small single-node cluster: +You can use a managed database, an operator such as CloudNativePG, or this minimal single-instance Deployment. If you +use your own, skip this file and set `DATABASE_URL` to it. -```yaml postgres.yaml +```yaml postgres.yaml lines expandable apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-data namespace: reactive-resume spec: - accessModes: - - ReadWriteOnce + accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi @@ -139,7 +86,7 @@ metadata: namespace: reactive-resume spec: replicas: 1 - # Stop the old database Pod before another one mounts the same data directory. + # Stop the old Pod before a new one mounts the same data. strategy: type: Recreate selector: @@ -152,7 +99,7 @@ spec: spec: containers: - name: postgres - image: postgres:17 + image: postgres:18 ports: - containerPort: 5432 env: @@ -165,15 +112,12 @@ spec: secretKeyRef: name: reactive-resume key: POSTGRES_PASSWORD - - name: PGDATA - value: /var/lib/postgresql/data/pgdata volumeMounts: - name: data - mountPath: /var/lib/postgresql/data + mountPath: /var/lib/postgresql readinessProbe: exec: command: ["pg_isready", "-h", "127.0.0.1", "-U", "postgres", "-d", "postgres"] - initialDelaySeconds: 10 periodSeconds: 10 volumes: - name: data @@ -190,34 +134,28 @@ spec: app.kubernetes.io/name: postgres ports: - port: 5432 - targetPort: 5432 ``` -- Keep the PostgreSQL Service a ClusterIP. Do not expose PostgreSQL to the public internet. -- Keep the image pinned to a PostgreSQL major version. `PGDATA` uses a subdirectory so filesystem entries such as - `lost+found` at the volume root do not prevent initialization. -- `POSTGRES_PASSWORD` initializes a new database only. Changing the Secret does not change an existing database's password. -- The app runs database migrations automatically on every start, and needs to reach PostgreSQL before it becomes ready. +- Keep this Service a ClusterIP. Don't expose PostgreSQL outside the cluster. +- `POSTGRES_PASSWORD` only applies when the database is first created. Changing it later doesn't change the password. +- Inside the cluster, use the Service name as the host in `DATABASE_URL`, never `localhost`. -## Deploy the application +## Deploy the app -With the namespace and Secret above, this file adds the uploads PersistentVolumeClaim, Deployment, Service, and Ingress. +This file adds the uploads volume, the Deployment, a Service and an Ingress. -```yaml reactive-resume.yaml -# Persistent storage for uploads, used when S3 is not configured +```yaml reactive-resume.yaml lines expandable apiVersion: v1 kind: PersistentVolumeClaim metadata: name: reactive-resume-data namespace: reactive-resume spec: - accessModes: - - ReadWriteOnce + accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi --- -# Deployment apiVersion: apps/v1 kind: Deployment metadata: @@ -225,7 +163,7 @@ metadata: namespace: reactive-resume spec: replicas: 1 - # One replica at a time: migrations run on startup and the PVC is ReadWriteOnce. + # The uploads volume is ReadWriteOnce, so the old Pod must stop first. strategy: type: Recreate selector: @@ -236,7 +174,7 @@ spec: labels: app.kubernetes.io/name: reactive-resume spec: - # The official image runs as the non-root `node` user (UID/GID 1000). + # The image runs as the non-root "node" user (UID and GID 1000). securityContext: runAsNonRoot: true runAsUser: 1000 @@ -244,22 +182,27 @@ spec: fsGroup: 1000 containers: - name: reactive-resume - image: ghcr.io/reactive-resume/reactive-resume:latest + image: ghcr.io/reactive-resume/reactive-resume:v6 imagePullPolicy: Always - # Docker Hub alternative: amruthpillai/reactive-resume:latest ports: - - containerPort: 3000 + - name: http + containerPort: 3000 envFrom: - secretRef: name: reactive-resume volumeMounts: - name: data mountPath: /app/data + startupProbe: + httpGet: + path: /api/health + port: http + periodSeconds: 5 + failureThreshold: 60 readinessProbe: httpGet: path: /api/health - port: 3000 - initialDelaySeconds: 30 + port: http periodSeconds: 10 timeoutSeconds: 5 resources: @@ -273,7 +216,6 @@ spec: persistentVolumeClaim: claimName: reactive-resume-data --- -# Service apiVersion: v1 kind: Service metadata: @@ -285,9 +227,8 @@ spec: ports: - name: http port: 80 - targetPort: 3000 + targetPort: http --- -# Ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -295,6 +236,11 @@ metadata: namespace: reactive-resume annotations: cert-manager.io/cluster-issuer: letsencrypt + # For ingress-nginx. Other controllers have equivalent settings. + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" spec: ingressClassName: nginx rules: @@ -307,204 +253,147 @@ spec: service: name: reactive-resume port: - number: 80 + name: http tls: - hosts: - resume.example.com secretName: reactive-resume-tls ``` - - Replace `resume.example.com`, `ingressClassName`, and the `cluster-issuer` name with your own host, controller class, - and configured issuer. Point your hostname's DNS at the Ingress controller. The app listens on `PORT` - and serves both the API and the built web app; the default image uses `PORT=3000`, so the example targets container - port `3000`. If you change `PORT`, update the container port, Service `targetPort`, and readiness probe to match. - +Replace `resume.example.com`, `ingressClassName` and the `cluster-issuer` with your own values, and point your DNS at +the Ingress controller. The annotations let the Ingress accept Assistant attachments of up to 25 MB and stream replies +that take up to 4 minutes; [What every reverse proxy needs](/self-hosting/examples#what-every-reverse-proxy-needs) +explains why. That section also covers client IP headers: make sure your controller doesn't pass on +`CF-Connecting-IP`, `CF-Connecting-IPv6` or `True-Client-IP` headers sent by clients, or they can get around per-IP +rate limits. -Apply the four files in order and wait for PostgreSQL before starting the app: - -```bash -kubectl apply -f namespace.yaml -kubectl apply -f secret.yaml -kubectl apply -f postgres.yaml -kubectl -n reactive-resume rollout status deployment/postgres --timeout=300s -kubectl apply -f reactive-resume.yaml -kubectl -n reactive-resume rollout status deployment/reactive-resume --timeout=300s -kubectl -n reactive-resume get pods -w -``` - -If you use an external database, skip `postgres.yaml` and its rollout check, and ensure the database is reachable first. - -The app Pod becomes `Ready` only after automatic migrations succeed and the `/api/health` endpoint reports the database -and storage healthy. If the Pod exits or stays in `CrashLoopBackOff`, check the logs: - -```bash -kubectl -n reactive-resume logs -f deployment/reactive-resume -``` - -## Storage: uploads and persistence - -Uploads are stored in one of two ways, exactly as in the Docker guide: - -- **Local storage (default)**. Unless all three of `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, and `S3_BUCKET` are set, - the app writes uploads under `/app/data`. The `reactive-resume-data` PVC is mounted there; `fsGroup: 1000` requests - group write access from storage drivers that support it. Otherwise, configure volume permissions for UID/GID `1000`. - Without that mount, uploads are lost when the Pod is replaced. -- **S3-compatible storage**. Set `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, and `S3_BUCKET` in the Secret. Set - `S3_REGION` for your bucket (default: `us-east-1`) and `S3_ENDPOINT` for non-AWS services. Set - `S3_FORCE_PATH_STYLE: "true"` for path-style services such as MinIO or SeaweedFS. You can then omit the app's uploads - PVC, volume, and volume mount. Private AI Agent attachments require S3-compatible storage. - - - Switching between local storage and S3 does not move existing uploads. Export or back them up before changing the - storage driver. - - -Back up the PostgreSQL database and the upload storage (the `reactive-resume-data` PVC or the S3 bucket) together, on a -regular schedule. Recreating the Deployment must preserve both. - -## Ingress and the public URL - -The Ingress above routes `resume.example.com` to the Service and terminates TLS with cert-manager. Two rules apply: - -- `APP_URL` must equal the public HTTPS URL users visit. A mismatch (or serving HTTPS while `APP_URL` says `http://…`) - causes sign-in redirects and cookies that do not stick. -- The app serves the web app, the API, uploads, and assets from one origin. Proxy the whole application; do not rewrite - or filter paths such as `/api/`. - - - HTTPS is strongly recommended. Authentication cookies and the first-user signup flow depend on a correct public - origin. - - -## Health checks and startup - -Reactive Resume exposes a health endpoint at `/api/health` that verifies the **database** and **storage**; if either is -unhealthy it returns HTTP `503`, and `200` when both are healthy. - -The Deployment uses this endpoint for **readiness**, keeping the Pod out of Service rotation until both dependencies -are healthy. It deliberately omits a liveness probe against this dependency check: restarting the app does not repair -a database or storage outage, and a slow migration should not be interrupted by a probe. Kubernetes restarts the -container if the server process exits. - -To check the endpoint manually: - -```bash -kubectl -n reactive-resume port-forward service/reactive-resume 3000:80 -``` - -```bash -curl -f http://localhost:3000/api/health -``` - - - On every start the server **automatically runs database migrations** before serving traffic. If migrations fail - (usually a database connection issue), the container exits with an error — check `kubectl logs`. - - -## Verify the installation +## Apply and verify - - Open `APP_URL` and sign up for the first account. Without SMTP configured, verification emails are logged to the - server console instead of being sent: `kubectl -n reactive-resume logs -f deployment/reactive-resume`. + + ```bash + kubectl apply -f namespace.yaml + kubectl apply -f secret.yaml + kubectl apply -f postgres.yaml + kubectl -n reactive-resume rollout status deployment/postgres --timeout=300s + kubectl apply -f reactive-resume.yaml + kubectl -n reactive-resume rollout status deployment/reactive-resume --timeout=600s + ``` + + The app Pod becomes `Ready` once its startup migrations finish and `/api/health` answers `200`. Follow along with + `kubectl -n reactive-resume logs -f deployment/reactive-resume`. - - Create a resume from the dashboard, add a few sections, and upload a profile picture. Reload the page and confirm - the saved content and picture are present. + + Open your `APP_URL` and create an account. You're signed in right away. Without SMTP, the verification email is + written to the app log instead of being sent. - - Open **Download** in the builder header and choose **PDF**. Builder PDF rendering happens in the browser with - the Forme PDF engine. Open the downloaded file and check its text, fonts, and picture. - - - - Replace the Pod and verify nothing is lost: + + Create a resume from a sample and upload a profile picture. Then replace the Pod: ```bash kubectl -n reactive-resume rollout restart deployment/reactive-resume - kubectl -n reactive-resume rollout status deployment/reactive-resume --timeout=300s + kubectl -n reactive-resume rollout status deployment/reactive-resume ``` - After the new Pod is `Ready`, sign in again and confirm the resume and any uploaded picture are still there. - If you deployed the example PostgreSQL Deployment, also restart it with `kubectl -n reactive-resume rollout restart - deployment/postgres`, wait for its rollout to complete, and confirm the same data remains. Expect downtime during - these single-replica restarts. + Reload the page. The resume and picture should still be there. Expect a short outage while a single-replica + Deployment restarts. - - For a private single-user instance, add `FLAG_DISABLE_SIGNUPS: "true"` under `stringData` in `secret.yaml`, apply it - with `kubectl apply -f secret.yaml`, and restart the app Deployment **after** your account exists. + + For a private instance, add `FLAG_DISABLE_SIGNUPS: "true"` to `secret.yaml` after your account exists, apply it, + and restart the Deployment. Pods only read Secret changes when they start. -## Community Helm chart (HelmForge) +## How the probes work -A community-maintained Helm chart for Reactive Resume is available in the HelmForge charts repository: +- The **startup probe** gives the first start up to 5 minutes to run migrations before other probes begin. +- The **readiness probe** calls `/api/health`, which checks the database, storage and, when configured, Redis. It + answers `503` if any of them fails, and Kubernetes stops sending traffic to that Pod until it recovers. +- There is deliberately no liveness probe on `/api/health`. Restarting the app doesn't fix a database or storage + outage. If the server process itself exits, Kubernetes restarts the container anyway. -- Chart source: [helmforgedev/charts — charts/reactive-resume](https://github.com/helmforgedev/charts/tree/main/charts/reactive-resume) -- Chart documentation: [helmforge.dev — Reactive Resume](https://helmforge.dev/docs/charts/reactive-resume) +See [Check the health endpoint](/self-hosting/docker#check-the-health-endpoint) for the response format. To call it +yourself: - - This chart is **community-maintained and lives outside this repository**. It is not part of the Reactive Resume - project, and chart support is handled in the HelmForge repository, not here. The manifests above work without it. - +```bash +kubectl -n reactive-resume port-forward service/reactive-resume 3000:80 +curl http://localhost:3000/api/health +``` -## Updating +## Run several replicas -1. **Back up the database and uploads first.** Do this before every update. -2. **Restart the app to pull the current `latest` image.** The example explicitly sets `imagePullPolicy: Always`; - setting the image to the same `latest` string does not trigger a rollout. +The example runs one replica because uploads live on a `ReadWriteOnce` volume. To run more: + +1. Switch uploads to S3-compatible storage: add the `S3_*` variables to the Secret, then remove the PVC, the volume and + the `/app/data` mount. See [Store uploads in S3-compatible storage](/self-hosting/examples#store-uploads-in-s3-compatible-storage). +2. Add Redis and set `REDIS_URL` in the Secret, so rate limits, stopping Assistant replies and live updates work + across Pods. +3. Change `strategy` to `RollingUpdate` and raise `replicas`. + +Pods that start together are safe: they take turns on migrations, and only one applies changes. + +## Update the app + +1. Back up the database and uploads. +2. Restart the Deployment to pull the newest `v6` image: ```bash kubectl -n reactive-resume rollout restart deployment/reactive-resume - ``` - -3. **Wait for the rollout**, then check the startup logs while migrations run: - - ```bash kubectl -n reactive-resume rollout status deployment/reactive-resume - kubectl -n reactive-resume logs -f deployment/reactive-resume ``` - - For reproducible deployments, pin a specific version tag or digest instead of `latest`, and update PostgreSQL - separately from the app, following your operator's or provider's upgrade procedure. For a pinned app image, change - `image` in `reactive-resume.yaml` and run `kubectl apply -f reactive-resume.yaml` to deploy the new version. - +3. Check the logs while the new version applies its migrations. + +For fully reproducible deployments, pin an exact tag such as `v6.0.0` (or an image digest), change it in +`reactive-resume.yaml`, and run `kubectl apply -f reactive-resume.yaml`. Coming from v5? Read +[Upgrading to v6](/self-hosting/upgrading-to-v6) first. + +Back up PostgreSQL and the uploads (the PVC or the S3 bucket) on a schedule, and keep a copy of the Secret. Update +PostgreSQL separately from the app, following your operator's or provider's upgrade process. + +## Community Helm chart + +A community-maintained Helm chart is available from HelmForge: + +- Chart source: [helmforgedev/charts](https://github.com/helmforgedev/charts/tree/main/charts/reactive-resume) +- Documentation: [helmforge.dev](https://helmforge.dev/docs/charts/reactive-resume) + + + The Helm chart is maintained outside the Reactive Resume project. Report chart problems in the HelmForge repository. + Check that it supports v6 before using it; the manifests above work without it. + ## Troubleshooting - - **Common cause**: database migrations failed (often a bad `DATABASE_URL`). - - **What to do**: check logs with `kubectl -n reactive-resume logs -f deployment/reactive-resume` and confirm the - PostgreSQL Pod is running and the Service is reachable. URL-encode special characters in the password. + Read the logs with `kubectl -n reactive-resume logs deployment/reactive-resume --previous`. `Invalid environment + variables` names a missing or malformed setting. `Database migrations failed` usually means a wrong + `DATABASE_URL` or PostgreSQL isn't ready. `Local storage path is not writable` means the volume isn't writable by + UID 1000; keep `fsGroup: 1000` or fix the volume's permissions. - - - **Common cause**: `APP_URL` does not match the URL you actually use, or you serve HTTPS while `APP_URL` says - `http://…`. - - **Fix**: set `APP_URL` to the canonical public HTTPS URL in the Secret, then restart the Deployment. + + Port-forward and call `/api/health`. The entry marked `unhealthy` points at the problem: the database, the + uploads volume or S3 settings, or Redis. - - - **Common cause**: storage health failed (not only the database). - - **Fix**: inspect the endpoint response payload and check the `storage` field; confirm the PVC is mounted and not - full, and that the S3 settings (if used) are valid. + + `APP_URL` doesn't match the address in the browser, or it says `http://` while you use `https://`. Fix it in the + Secret, apply, and restart the Deployment. - - - **Cause**: local upload storage was not mounted to a persistent volume. - - **Fix**: add the `reactive-resume-data` PVC mount at `/app/data` (with `fsGroup: 1000`) and redeploy. - - - - - **Checks**: for builder exports, inspect the browser console and failed network requests, including fonts and - images. Check download permissions, browser memory limits, extensions, and custom CSP rules. - - No external Browserless or Chromium service is needed. API PDF downloads and the public viewer's server fallback - render in the app process; inspect the app logs if one of those requests fails. + + The Ingress is buffering responses, timing out after 60 seconds, or rejecting large bodies. Apply the annotations + from the example, or your controller's equivalents. + +## Related pages + +- [Environment variables](/self-hosting/environment-variables): every setting you can put in the Secret. +- [Self-hosting with Docker](/self-hosting/docker): the same setup with Docker Compose, plus backups. +- [Deployment examples](/self-hosting/examples): S3 storage, Redis, local AI and private instances. diff --git a/docs/self-hosting/migration.mdx b/docs/self-hosting/migration.mdx index 0c70f47f3..be7efc34a 100644 --- a/docs/self-hosting/migration.mdx +++ b/docs/self-hosting/migration.mdx @@ -1,363 +1,210 @@ --- -title: "Migrating from v4 to v5" -description: "Step-by-step guide to migrate a self-hosted Reactive Resume instance from v4 to v5, covering database schema changes, manual and automated options." +title: "Migrating from v4" +description: "Move users and resumes from a self-hosted Reactive Resume v4 instance to the current version, by importing resumes one by one or with the migration scripts." --- -## Overview - -To move a Reactive Resume installation from **v4 to v5**, you set up a new v5 instance alongside your existing v4 instance, then transfer your users and resumes across. - - - This page is for **v4 → v5 data migration** only. For normal v5 upgrades, use the [Self-Hosting with - Docker](/self-hosting/docker) guide. v5 schema migrations run automatically on app startup. - +Reactive Resume v4 used a different database schema, so you can't upgrade a v4 installation in place. Instead, you set up a new installation next to it and copy your users and resumes across. This page covers that move. To upgrade an existing v5 installation, see [Upgrading to v6](/self-hosting/upgrading-to-v6) instead. - This guide applies only to infrastructure and backups you are authorized to operate. It does not grant access to - hosted Reactive Resume data. Only the hosted service operator can verify whether a hosted snapshot exists and - authorize access to it. + Keep your v4 instance running until every user and resume is in the new installation and you've checked the result. It's your fallback if something goes wrong. Work only on infrastructure and backups you're authorized to operate. - - **Keep your v4 instance running** until you have migrated all data to v5 and checked that everything works. That way - you have a fallback if the migration goes wrong. - +## Before you start -## Prerequisites +You need: -Before starting the migration, ensure you have: +- Your running v4 instance, and a recent backup of its PostgreSQL database. +- Access to the v4 database (the source) and to a new, empty PostgreSQL database for the new installation (the target). +- A plan for the new installation. [Self-hosting with Docker](/self-hosting/docker) is the usual choice. - - Your existing Reactive Resume v4 instance should be running and accessible. - - A fresh Reactive Resume v5 instance set up and running. Follow the [Self-hosting with Docker](/self-hosting/docker) - guide if you haven't done this yet. - - - Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target). - - A recent backup of your v4 database. Always back up before any migration. - +## Choose a method -## Choosing a migration method +| Method | Best for | How it works | +| --- | --- | --- | +| [Import resumes one by one](#import-resumes-one-by-one) | A handful of resumes | Each person exports their v4 resumes as JSON and imports them into the new installation. | +| [Run the migration scripts](#run-the-migration-scripts) | Many users and resumes | Scripts copy users, sign-in methods, resumes, and statistics straight from database to database. | -The best migration approach depends on the size of your instance: +## Import resumes one by one - - - **Best for**: Small instances with a handful of resumes. Uses the built-in Import Dialog to manually convert resumes - one at a time. - - - **Best for**: Large instances with many users and resumes. Uses migration scripts to batch-process all users and - resumes automatically. - - - -## Recover one owner's resumes without overwriting v5 - -Use a recovery case when an owner changed resumes after an earlier migration. Keep the case record private and outside -Git because it may connect account and resume identifiers. Record these fields before inspecting resume content: - -- Recovery case ID -- Source snapshot capture time -- Owner verification status -- Source resume ID and mapped target resume ID, if one exists -- Source and target content hashes -- Proposed outcome: `no-op`, `export-copy`, or `blocked` - -Case IDs, source resume IDs, and non-null target resume IDs must contain a non-whitespace character after trimming and -must not contain Unicode control or format characters. Valid identifiers are preserved verbatim. - -These hashes prove content equality only; they never prove ownership, source authenticity, or recipient identity. - -An authorized operator should follow this order: - - - - Confirm that a source snapshot exists and record when it was captured. If no source snapshot exists, report that - factual limit. Recovery tooling cannot reconstruct records that are absent from every available source. - - - - Verify the requester using the operator's approved account-ownership process. Confirm the old-to-new owner mapping; - a matching email address, resume title, or username alone is not ownership proof. Stop if either check is incomplete. - - - - Build one serialized JSON comparison request containing the case IDs, safety flags, and source and target values. The - comparator accepts only this request string, not an object argument. Source and target data must already conform - exactly to the current v5 resume-data schema before content hashes are calculated. Raw v4 exports are unsupported, - and the comparator performs no format conversion. Identical content is a `no-op`. Source-only or divergent content - is an `export-copy`. Missing identity evidence, mapping, valid current-v5 data, or a source snapshot is `blocked`. - - - - Default to a private JSON export. Never overwrite the current v5 resume. After the recipient and delivery channel are - approved, deliver the recovered JSON privately so the owner can import it as a separate resume. Record source and - delivered hashes outside Git and confirm they match. - - - -Repository contributors can rehearse this decision with the pure comparator in -`tooling/recovery/compare-resume.ts`. It accepts one serialized JSON comparison request and rejects object arguments. -The request's source and target values must already conform exactly to the current v5 resume-data schema. It does not -accept raw v4 exports or perform legacy conversion; review the historical converter at tag `v5.0.20` separately before -processing legacy-format data. The comparator produces a deterministic dry-run manifest and has no database, network, -or write path. Use synthetic IDs and content only; keep any operational manifest outside the repository. - -## Manual migration (small instances) - -If you have only a few resumes to migrate, the simplest approach is to use the **Import Dialog** feature in v5. +The current version reads v4 JSON exports directly and converts them. - In your v4 instance, go to each resume and export it as JSON. This creates a portable file containing all your resume data. + In your v4 instance, open each resume and export it as JSON. - - - In your new v5 instance: 1. Sign in or create a new account 2. Click **Create Resume** or use the **Import** option 3. - Select the **Reactive Resume v4** format 4. Upload your exported JSON file The import process automatically converts - the v4 format to v5. - - - - Review the imported resume to ensure all data transferred correctly. Repeat for each resume you need to migrate. + + Sign in to the new installation (create an account first if needed), select **New**, then **Import a resume**, and choose the JSON file. Reactive Resume recognizes the v4 format on its own. See [Importing resumes](/guides/importing-resumes) for details. + + + Look through the imported resume, especially dates and layout, then repeat for the next one. - - The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences - between v4 and v5. - +## Run the migration scripts -## Automated migration (large instances) +The scripts live in the repository at tag `v5.0.20`, the last release that includes them. They were written against the database schema of that release, so you create the target database with that release first, run the scripts, and only then start the current version, which brings the data forward with its own migrations. -For instances with many users and resumes, use the migration scripts to automate the process. The migration happens in two phases: first users, then resumes. + + Don't run the scripts against a database that a newer release has already set up. They don't fill in columns added later (such as the sign-in `issuer` added in v5.2.8), so migrated users could fail to sign in. Starting the current version after the scripts fills those columns in. + -### Requirements +### Prepare the scripts -To run the migration scripts, you need the following installed on your host machine: +Install `tsx` and a `.env` loader such as `dotenvx` on the machine that runs the scripts, then check out the tag: - - - **tsx** - TypeScript execution environment. Install globally with: ```bash npm install -g tsx ``` - - - **dotenvx** (or any tool to load `.env` files). Install globally with: ```bash npm install -g @dotenvx/dotenvx ``` - Alternatively, you can use `dotenv`, `direnv`, or export the variables manually. - - - Clone the Reactive Resume repository and check out the last tag that includes the migration scripts: - ```bash - git clone https://github.com/reactive-resume/reactive-resume.git reactive-resume - cd reactive-resume - git checkout tags/v5.0.20 - pnpm install - ``` - - +```bash +npm install -g tsx @dotenvx/dotenvx +git clone https://github.com/reactive-resume/reactive-resume.git reactive-resume-migration +cd reactive-resume-migration +git checkout tags/v5.0.20 +pnpm install +``` - - The v4 migration scripts live in the `v5.0.20` tag. Use that checkout only to run the migration scripts against your - v4 and v5 databases; keep your actual v5 deployment on the latest version. - +Use this checkout only to run the migration. Your actual installation runs the current release. -### Environment setup - -Create a `.env` file in the root of the repository with the following variables: +Create a `.env` file at the root of the checkout: ```bash .env -# Connection string to your NEW v5 PostgreSQL database (target) -DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resume_v5" +# The NEW, empty database (target) +DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resume" -# Connection string to your OLD v4 PostgreSQL database (source) +# The OLD v4 database (source) PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resume_v4" ``` - Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and - `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss. + Double-check both connection strings. `DATABASE_URL` is the new target and `PRODUCTION_DATABASE_URL` is the old v4 source. Swapping them can destroy data. `PRODUCTION_DATABASE_URL` is used only by these scripts, never by the app. -`PRODUCTION_DATABASE_URL` is used only by these migration scripts. It is not a runtime app variable. +### Create the target schema -### Step 1: Migrate users +Apply the `v5.0.20` migrations to the empty target database: -The user migration script transfers all user accounts, authentication data, and two-factor settings from v4 to v5. +```bash +dotenvx run -- pnpm db:migrate +``` + +### Migrate users ```bash dotenvx run -- tsx scripts/migration/user.ts ``` -**What this script does:** +The script: -- Fetches users in batches from the v4 database -- Creates corresponding user accounts in the v5 database -- Migrates authentication providers (email, Google, GitHub, custom OAuth) -- Preserves two-factor authentication settings and backup codes -- Creates a mapping file (`scripts/migration/user-id-map.json`) that links old user IDs to new ones +- Reads v4 users in batches and creates matching users in the target. +- Copies sign-in methods (email and password, Google, GitHub, custom OAuth). Two-factor authentication isn't copied: every migrated user starts with it turned off. +- Writes `scripts/migration/user-id-map.json`, which links each v4 user ID to its new ID. The resume script needs it. +- Saves progress to `scripts/migration/user-progress.json` after each batch. If the run stops, run it again to continue from the last saved batch. - - The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where - it left off. - +It ends with a summary of users created, accounts created, and users skipped. -**Expected output:** - -``` -⌛ Starting user migration... -📥 Fetching users batch from production database (OFFSET 0)... -📋 Found 1000 users in this batch. -📝 Preparing to bulk insert 1000 users... -✅ Bulk inserted 1000 users in 245.3 ms (avg 0.2 ms/user) -💾 Progress saved at offset 1000 -📦 Processed 1000 users so far... - -📊 Migration Summary: - Users created: 1000 - Accounts created: 1000 - Two-factor entries created: 50 - Skipped (already exist): 0 -⏱️ Total migration time: 1234.5 ms (1.23 seconds) -✅ User migration complete! -``` - -### Step 2: Migrate resumes - -After users are migrated, run the resume migration script. This script depends on the user ID mapping created in the previous step. +### Migrate resumes ```bash dotenvx run -- tsx scripts/migration/resume.ts ``` -**What this script does:** +The script: -- Fetches resumes in batches from the v4 database -- Converts each resume from v4 format to v5 format automatically -- Links resumes to the correct users using the ID mapping -- Migrates resume statistics (views, downloads) -- Preserves visibility settings (public/private) and lock status +- Reads v4 resumes in batches, converts each to the new format, and assigns it to the right user through the ID map. +- Copies view and download statistics, public or private visibility, and the locked state. +- Saves progress to `scripts/migration/resume-progress.json` after each batch, and continues from there if you run it again. -Like the user script, the resume migration also saves progress and can be resumed if interrupted. - -**Expected output:** - -``` -⌛ Starting resume migration... -📥 Fetching resumes batch from production database (OFFSET 0)... -📋 Found 2500 resumes in this batch. -📝 Preparing to bulk insert 2500 resumes... -✅ Bulk inserted 2500 resumes in 892.1 ms (avg 0.4 ms/resume) -💾 Progress saved at offset 2500 -📦 Processed 2500 resumes so far... - -📊 Migration Summary: - Resumes created: 2500 - Statistics created: 2500 - Skipped (userId not found or already exist): 0 - Errors: 0 -⏱️ Total migration time: 5678.9 ms (5.68 seconds) -✅ Resume migration complete! -``` - -### Progress and recovery - -Both migration scripts support graceful shutdown and resume: - -- **Progress files**: `scripts/migration/user-progress.json` and `scripts/migration/resume-progress.json` track the current migration state -- **User ID mapping**: `scripts/migration/user-id-map.json` maps v4 user IDs to v5 user IDs -- **Graceful shutdown**: Press `Ctrl+C` to stop the migration safely. Progress is saved before exit. -- **Resume migration**: Run the script again to continue from where you left off +It ends with a summary of resumes created, resumes skipped, and errors. - Preserve progress files and the user ID mapping as recovery evidence. Do not delete them or replay the historical - scripts against a populated target as a recovery shortcut. Review the `v5.0.20` scripts, source backup, mapping, and - target state before any rerun. + Each script deletes its progress file when it finishes. Keep `user-id-map.json` as the record of which v4 user became which new user. Don't re-run the scripts against a populated target to "fix" a problem; review the source backup, the map, and the target first. -## Post-migration steps +### Start the current version -After completing the migration: +Point a current Reactive Resume installation at the target database and start it. On startup it applies every migration since `v5.0.20`, which also fills in the new columns for the migrated users. Watch the logs for `Database migrations completed`. + +## After the migration - - Sign in to your v5 instance and spot-check several user accounts and resumes to ensure data transferred correctly. + + Sign in as a few users and spot-check their resumes. - - - - Create a test resume and export it as PDF - Verify social sign-in works (if configured) - Check that two-factor - authentication works for migrated users - - - - Once verified, update your DNS records or reverse proxy to point to the new v5 instance. - - - - After confirming everything works and allowing a grace period, you can safely shut down your v4 instance. + + Download a resume as PDF, and sign in with email and password and with each social provider you use. + + + Point your DNS or reverse proxy at the new installation. + + + After a grace period with no problems, shut down the v4 instance. -## Important notes +## What carries over - - Users who signed up with email/password can continue using their existing passwords. No password reset is required after migration. + + People who signed up with email and password keep their password. No reset is needed. + + + The database stores references to pictures, not the files. Give the new installation access to the same storage (for example the same S3 bucket), or people re-upload their pictures. + + + Configure the same providers with the same client IDs in the new installation. Custom OAuth now uses a different callback URL, so update your provider as described in [Single sign-on (SSO)](/self-hosting/sso). On the first sign-in, the provider account is linked to the migrated user by email. For custom OAuth, that works only if the user's email was verified in v4; otherwise the sign-in stops with an "account not linked" error. + + + Not carried over. Users who had two-factor authentication in v4 sign in with their password alone, and can set it up again in **Settings → Account**. - - - User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5 - instance has access to the same bucket, or users may need to re-upload their avatars. - - - - Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider - migrating your storage bucket or updating references as needed. - - - - If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users - authenticate with the same provider ID, so mismatched configurations will cause login failures. - - - The v5 schema has some changes from v4: - - `visibility` (public/private) is now `isPublic` (boolean) - - Resume `title` is now `name` - - Some resume data fields have been reorganized - - The migration scripts handle these conversions automatically. + v4's `visibility` became a public/private flag, a resume's `title` became its `name`, and resume data was reorganized. The scripts and the importer handle these conversions. +## Recover one person's resumes later + +Sometimes a person changes a resume in v4 after the main migration, or a resume didn't come across. Handle this as a recovery case rather than re-running the scripts, and never overwrite the resume in the new installation. + +Keep a private record for each case, outside Git, because it links account and resume identifiers: + +- Case ID and the capture time of the source snapshot. +- How you verified the person owns the account. +- Source resume ID and mapped target resume ID, if there is one. +- Source and target content hashes, and the outcome: `no-op`, `export-copy`, or `blocked`. + + + + Check that a source snapshot exists and note when it was taken. Records missing from every available source can't be reconstructed. + + + Verify the requester through your approved account-ownership process, and confirm the old-to-new user mapping. A matching email, resume title, or username alone isn't proof. Stop if either check is incomplete. + + + Identical content is a `no-op`. Content that exists only in the source, or differs, is an `export-copy`. Missing evidence, mapping, valid data, or source is `blocked`. Content hashes prove equality only, never ownership or authenticity. + + + Send the recovered JSON privately through an approved channel, so the person imports it as a new resume. Confirm the delivered hash matches the source. + + + +Contributors can rehearse the comparison with `tooling/recovery/compare-resume.ts`. It takes one serialized JSON request whose source and target already match the current resume schema, produces a dry-run manifest, and never touches a database or the network. It doesn't convert raw v4 data; review the converter at tag `v5.0.20` for that. Use synthetic data only. + ## Troubleshooting - - Ensure your `.env` file contains both `DATABASE_URL` and `PRODUCTION_DATABASE_URL`, and that you're using a tool like `dotenvx` to load them before running the script. + + Put both `DATABASE_URL` and `PRODUCTION_DATABASE_URL` in `.env` and run the scripts through `dotenvx run --` (or export the variables yourself). - - - Users are skipped if: - Their email already exists in the v5 database - Their username already exists in the v5 - database - They were already migrated in a previous run Check the console output for skip reasons. - - - - Resumes are skipped if: - The associated user wasn't migrated (user ID not in mapping file) - A resume with the same - slug already exists for that user - They were already migrated in a previous run - - - - Historical scripts can create default empty data when a v4 resume cannot be parsed. Treat that result as a failed - conversion, not a recovered resume. Preserve the source export, review the `v5.0.20` converter, and use the Import - Dialog only after valid source data is confirmed. - - - - The scripts process data in batches to avoid overwhelming the database. For very large instances: - - Consider running the migration during off-peak hours - - Ensure both databases have adequate resources - - The batch size can be adjusted in the script files if needed + + A user is skipped when their email or username already exists in the target, or when an earlier run already migrated them. The console output gives the reason. + + + A resume is skipped when its owner isn't in the user ID map, when the owner already has a resume with the same slug, or when an earlier run already migrated it. + + + When a v4 resume can't be parsed, the scripts can create empty default data. Treat that as a failed conversion, not a recovered resume. Keep the source export and import it by hand once you've confirmed it's valid. + + + Check that you started the current version against the target database after the scripts, so its migrations ran. The startup log shows `Database migrations completed`. + + + The scripts work in batches to spare the databases. Run them off-peak, give both databases enough resources, or adjust the batch size in the script files. diff --git a/docs/self-hosting/sso.mdx b/docs/self-hosting/sso.mdx index d908bc7cf..6f7e94e1a 100644 --- a/docs/self-hosting/sso.mdx +++ b/docs/self-hosting/sso.mdx @@ -1,373 +1,239 @@ --- -title: "Single Sign-On (SSO)" -description: "Configure Single Sign-On for self-hosted Reactive Resume using custom OAuth providers like Authentik, Authelia, Keycloak, or any OIDC-compliant IdP." +title: "Single sign-on (SSO)" +description: "Let people sign in to your self-hosted Reactive Resume through Authentik, Authelia, Keycloak, or any OpenID Connect or OAuth 2.0 identity provider." --- -## Overview - -Reactive Resume supports custom OAuth providers, so you can sign in through an enterprise identity provider or a self-hosted authentication service. This helps organizations that want to: - -- Use a centralized identity provider (Authentik, Authelia, Keycloak, etc.) -- Enforce Single Sign-On (SSO) across all internal applications -- Integrate with existing LDAP/Active Directory infrastructure +This guide connects a self-hosted Reactive Resume to your own identity provider, so people sign in with their company or homelab account. It works with any OpenID Connect (OIDC) provider, and with plain OAuth 2.0 providers that expose a user-info endpoint. You configure it with environment variables and a restart; there's nothing to set up in the app. - Custom OAuth is for **self-hosted instances**. If you're using the hosted version at - [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options. + This is for self-hosted installations. On [rxresu.me](https://rxresu.me), use the built-in sign-in options. -## Environment variables +## Before you start -To enable a custom OAuth provider, you need to configure the following environment variables in your `.env` file: +You need: -### Required variables +- Admin access to your identity provider, so you can register an application (client). +- The public address of your installation, set as `APP_URL`, for example `https://resume.example.com`. Every callback URL is built from it. +- A provider that returns an email address for each user. Reactive Resume identifies accounts by email and refuses sign-ins without one. -| Variable | Description | -| --------------------- | ------------------------------------------------- | -| `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider | -| `OAUTH_CLIENT_SECRET` | The client secret provided by your OAuth provider | +## Register Reactive Resume with your provider -### Endpoint configuration +Create a confidential OAuth 2.0 / OIDC client in your provider with these settings: -You must configure endpoints using **one** of these two methods: +| Setting | Value | +| --- | --- | +| Redirect URI (callback URL) | `{APP_URL}/api/auth/callback/custom`, for example `https://resume.example.com/api/auth/callback/custom` | +| Grant type | Authorization code | +| Scopes | `openid profile email` | + +Copy the client ID and client secret. The callback must match exactly, including `https`, the port, and the absence of a trailing slash. + + + Upgrading from a release before v5.2.8? The callback path changed from `/api/auth/oauth2/callback/custom` to `/api/auth/callback/custom`. Update the redirect URI in your provider, or sign-in fails after the upgrade. + + +## Configure the environment variables + +Set the client credentials and one way of finding the provider's endpoints, then restart Reactive Resume. - - For OIDC-compliant providers (most modern identity providers), you only need to set the discovery URL: - - | Variable | Description | - |----------|-------------| - | `OAUTH_DISCOVERY_URL` | Your provider's `.well-known/openid-configuration` URL | - - The discovery URL automatically provides the authorization, token, and userinfo endpoints. - - **Examples:** - - Authentik: `https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration` - - Keycloak: `https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration` - - Authelia: `https://auth.example.com/.well-known/openid-configuration` + + Most modern providers publish a discovery document. Point Reactive Resume at it and it reads the authorization, token, and user-info endpoints from there. + ```bash .env + OAUTH_PROVIDER_NAME="Company SSO" + OAUTH_CLIENT_ID="your-client-id" + OAUTH_CLIENT_SECRET="your-client-secret" + OAUTH_DISCOVERY_URL="https://sso.example.com/.well-known/openid-configuration" + ``` + + For providers without discovery, set all three endpoint URLs. - - For providers that don't support OIDC discovery, you must set all three URLs: - - | Variable | Description | - |----------|-------------| - | `OAUTH_AUTHORIZATION_URL` | The URL where users are redirected to authorize | - | `OAUTH_TOKEN_URL` | The URL to exchange authorization codes for tokens | - | `OAUTH_USER_INFO_URL` | The URL to fetch user profile information | - + ```bash .env + OAUTH_PROVIDER_NAME="Company SSO" + OAUTH_CLIENT_ID="your-client-id" + OAUTH_CLIENT_SECRET="your-client-secret" + OAUTH_AUTHORIZATION_URL="https://sso.example.com/oauth/authorize" + OAUTH_TOKEN_URL="https://sso.example.com/oauth/token" + OAUTH_USER_INFO_URL="https://sso.example.com/oauth/userinfo" + ``` -### Optional variables +| Variable | Required | Description | +| --- | --- | --- | +| `OAUTH_CLIENT_ID` | Yes | Client ID from your provider. | +| `OAUTH_CLIENT_SECRET` | Yes | Client secret from your provider. | +| `OAUTH_DISCOVERY_URL` | One of the two methods | The provider's `/.well-known/openid-configuration` URL. | +| `OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, `OAUTH_USER_INFO_URL` | One of the two methods | All three, when the provider has no discovery document. | +| `OAUTH_PROVIDER_NAME` | No | Label on the sign-in button and in **Settings**. Default `Custom OAuth`. | +| `OAUTH_SCOPES` | No | Space-separated scopes. Default `openid profile email`. | -| Variable | Description | Default | -| ------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- | -| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` | -| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` | - -## Callback URL - -When configuring your OAuth provider, you'll need to set the **callback URL** (also called redirect URI). Use the following format: - -``` -{APP_URL}/api/auth/callback/custom -``` - -For example, if your `APP_URL` is `https://resume.example.com`, the callback URL would be: - -``` -https://resume.example.com/api/auth/callback/custom -``` +All URLs must use `http` or `https`. On Docker, restart the container after changing `.env`; on Vercel, add the variables to the project and redeploy. - Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause - authentication to fail. + The sign-in button appears as soon as `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, but sign-in works only when `OAUTH_DISCOVERY_URL` or all three manual URLs are also set. If the button shows and does nothing useful, check the endpoint variables. - - **Upgrading an existing install:** the callback path changed from `/api/auth/oauth2/callback/custom` to - `/api/auth/callback/custom`. Update the redirect URI registered with your identity provider, or custom OAuth - sign-in will fail after the upgrade. - +## Check that it works - - **Upgrading an existing install with `OAUTH_DISCOVERY_URL`:** accounts are now identified by the issuer your - provider advertises, and the automatic migration cannot know that value ahead of time. It backfills existing - rows with the placeholder `local:oauth:custom`. After upgrading, run the following once, replacing the value - with the `issuer` field from your discovery document (`{OAUTH_DISCOVERY_URL}` returns it as JSON): +1. Open your installation's sign-in page. Under **or continue with**, a button with a key icon shows your `OAUTH_PROVIDER_NAME`. +2. Select it, sign in at your provider, and approve access. You land on **Documents**. +3. In **Settings → Account**, the **Sign-in & security** section lists your provider as **Connected**. - ```sql - UPDATE account SET issuer = 'https://auth.example.com/realms/main' WHERE provider_id = 'custom'; - ``` +People who already have an account can add or remove SSO from the same place with **Connect** and **Disconnect**. See [Linking social accounts](/guides/linking-social-accounts). - Skipping this does not lose data, but existing users signing in through your provider will no longer match - their account. If you configure the provider with explicit `OAUTH_AUTHORIZATION_URL` / `OAUTH_TOKEN_URL` - endpoints instead of discovery, the placeholder is already correct and no action is needed. - +## How profiles are mapped - - Built-in providers (Google, GitHub, LinkedIn) use callback URLs in this format: `{APP_URL}/api/auth/callback/ - {provider}` (for example `.../google`, `.../github`, `.../linkedin`). - +When someone signs in for the first time, Reactive Resume creates their account from the provider's profile: -## URL and proxy requirements +| Reactive Resume field | Taken from, in order | +| --- | --- | +| Email (required) | `email` | +| Name | `name`, then `preferred_username`, then the part of the email before `@` | +| Username | `preferred_username`, then the part of the email before `@`. A number is added if it's taken. | +| Picture | `image`, then `picture`, then `avatar_url` | -- Set `APP_URL` to the exact public URL users access (prefer HTTPS in production). -- Auth metadata, JWKS, and OAuth callback URLs are derived from `APP_URL`. -- Behind a reverse proxy, forward `Host` and `X-Forwarded-Proto` correctly, or cookie/session behavior may break. -- `trustedOrigins` are derived from `APP_URL`, so alternate domains are not automatically trusted. +If the email already belongs to an account, the sign-in is linked to that account, which keeps its name and username. This works only when that account's email address is verified in Reactive Resume. If it isn't (common on installations without SMTP, where verification emails never arrive), the sign-in stops with an "account not linked" error. -## Profile mapping +## Make SSO the only way in -Reactive Resume maps user profile data from the OAuth provider using these fields: +Two feature flags turn Reactive Resume into an SSO-only installation: -| Reactive Resume Field | OAuth Profile Fields (in order of preference) | -| --------------------- | --------------------------------------------- | -| **Email** (required) | `email` | -| **Name** | `name` → `preferred_username` → email prefix | -| **Username** | `preferred_username` → email prefix | -| **Avatar** | `image` → `picture` → `avatar_url` | +| Variable | Effect | +| --- | --- | +| `FLAG_DISABLE_EMAIL_AUTH=true` | Hides email-and-password sign-in and registration. | +| `FLAG_DISABLE_SIGNUPS=true` | Blocks new accounts through every method, including SSO. Existing users can still sign in. | - - The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error. - +Set only `FLAG_DISABLE_EMAIL_AUTH` if new people should still get an account on their first SSO sign-in. -## Provider-specific setup +## Provider examples -### Authentik +Replace the hostnames with your own. The redirect URI is always `{APP_URL}/api/auth/callback/custom`. - - - In the Authentik admin interface, navigate to **Applications → Providers** and create a new **OAuth2/OpenID Provider**. + + + 1. In the admin interface, open **Applications → Providers** and create an **OAuth2/OpenID Provider**. Set **Client type** to **Confidential** and add the redirect URI. + 2. Open **Applications → Applications**, create an application with the slug `reactive-resume`, and select the provider. + 3. Copy the client ID and secret from the provider. - - **Name**: Reactive Resume - - **Authorization flow**: Use your preferred authorization flow - - **Client type**: Confidential - - **Redirect URIs**: `https://resume.example.com/api/auth/callback/custom` + ```bash .env + OAUTH_PROVIDER_NAME="Authentik" + OAUTH_CLIENT_ID="your-client-id" + OAUTH_CLIENT_SECRET="your-client-secret" + OAUTH_DISCOVERY_URL="https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration" + ``` + - + + Add a client to Authelia's `configuration.yml`. Store a hashed secret there, generated with `authelia crypto hash generate pbkdf2 --variant sha512`. - - Navigate to **Applications → Applications** and create a new application: + ```yaml + identity_providers: + oidc: + clients: + - client_id: reactive-resume + client_name: Reactive Resume + client_secret: "the-hashed-secret" + public: false + authorization_policy: two_factor + redirect_uris: + - https://resume.example.com/api/auth/callback/custom + scopes: + - openid + - profile + - email + token_endpoint_auth_method: client_secret_post + ``` - - **Name**: Reactive Resume - - **Slug**: `reactive-resume` - - **Provider**: Select the provider you just created + Give Reactive Resume the **plain-text** secret, not the hash: - + ```bash .env + OAUTH_PROVIDER_NAME="Authelia" + OAUTH_CLIENT_ID="reactive-resume" + OAUTH_CLIENT_SECRET="the-plain-text-secret" + OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration" + ``` + -From the provider settings, copy the **Client ID** and **Client Secret**. + + 1. In your realm, open **Clients → Create client** and set **Client ID** to `reactive-resume`. + 2. Turn **Client authentication** on and keep **Standard flow** enabled. + 3. Add the redirect URI under **Valid redirect URIs**. + 4. Copy the secret from the **Credentials** tab. - -```bash .env -OAUTH_PROVIDER_NAME="Authentik" -OAUTH_CLIENT_ID="your-client-id" -OAUTH_CLIENT_SECRET="your-client-secret" -OAUTH_DISCOVERY_URL="https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration" -``` - - + ```bash .env + OAUTH_PROVIDER_NAME="Keycloak" + OAUTH_CLIENT_ID="reactive-resume" + OAUTH_CLIENT_SECRET="your-client-secret" + OAUTH_DISCOVERY_URL="https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration" + ``` + + -### Authelia +## Built-in providers - - - Add a client configuration to your Authelia `configuration.yml`: +Google, GitHub, and LinkedIn sign-in each turn on when both of their variables are set, and can run alongside your own provider: -```yaml -identity_providers: - oidc: - clients: - - client_id: reactive-resume - client_name: Reactive Resume - client_secret: "your-hashed-secret" # Use authelia hash-password to generate - public: false - authorization_policy: two_factor # or one_factor - redirect_uris: - - https://resume.example.com/api/auth/callback/custom - scopes: - - openid - - profile - - email - token_endpoint_auth_method: client_secret_post +| Provider | Variables | Callback URL | +| --- | --- | --- | +| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | `{APP_URL}/api/auth/callback/google` | +| GitHub | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | `{APP_URL}/api/auth/callback/github` | +| LinkedIn | `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` | `{APP_URL}/api/auth/callback/linkedin` | + +## Upgrading an install that uses OIDC discovery + +Since v5.2.8, linked accounts are identified by the issuer your provider advertises. The database migration that introduced this can't know your issuer, so it filled existing SSO accounts with the placeholder `local:oauth:custom`. If you used `OAUTH_DISCOVERY_URL` before v5.2.8, run this once after upgrading, with the `issuer` value from your discovery document: + +```sql +UPDATE account SET issuer = 'https://auth.example.com/realms/main' WHERE provider_id = 'custom'; ``` - - Generate the hashed secret using: `authelia crypto hash generate pbkdf2 --variant sha512` - +Without it, existing users who sign in through your provider no longer match their account. No data is lost. Installations that use the three manual URLs already have the right value and need nothing. - +## URLs and reverse proxies - -```bash .env -OAUTH_PROVIDER_NAME="Authelia" -OAUTH_CLIENT_ID="reactive-resume" -OAUTH_CLIENT_SECRET="your-plain-secret" -OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration" -``` - - - Use the **plain text** secret in Reactive Resume's environment, not the hashed version used in Authelia's configuration. - - - - - -### Keycloak - - - - In the Keycloak admin console: - - 1. Select your realm - 2. Navigate to **Clients → Create client** - 3. Set **Client ID** (e.g., `reactive-resume`) - 4. Set **Client authentication** to **On** - 5. Enable **Standard flow** - - - - - In the client settings, add the redirect URI: - - - **Valid redirect URIs**: `https://resume.example.com/api/auth/callback/custom` - - - -Go to the **Credentials** tab and copy the **Client secret**. - - -```bash .env -OAUTH_PROVIDER_NAME="Keycloak" -OAUTH_CLIENT_ID="reactive-resume" -OAUTH_CLIENT_SECRET="your-client-secret" -OAUTH_DISCOVERY_URL="https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration" -``` - - - -### Generic OIDC provider - -For any other OIDC-compliant provider: - -```bash .env -OAUTH_PROVIDER_NAME="My SSO" -OAUTH_CLIENT_ID="your-client-id" -OAUTH_CLIENT_SECRET="your-client-secret" -OAUTH_DISCOVERY_URL="https://sso.example.com/.well-known/openid-configuration" -``` - -### Non-OIDC provider (manual configuration) - -For providers that don't support OIDC discovery: - -```bash .env -OAUTH_PROVIDER_NAME="Custom Provider" -OAUTH_CLIENT_ID="your-client-id" -OAUTH_CLIENT_SECRET="your-client-secret" -OAUTH_AUTHORIZATION_URL="https://provider.example.com/oauth/authorize" -OAUTH_TOKEN_URL="https://provider.example.com/oauth/token" -OAUTH_USER_INFO_URL="https://provider.example.com/oauth/userinfo" -OAUTH_SCOPES="openid profile email" -``` - -## Complete example - -Here's a complete `.env` snippet showing custom OAuth alongside other authentication options: - -```bash .env -# --- Authentication --- -AUTH_SECRET="your-32-byte-hex-secret" - -# Built-in Social Auth (optional, can coexist with custom OAuth) -# GOOGLE_CLIENT_ID="" -# GOOGLE_CLIENT_SECRET="" -# GITHUB_CLIENT_ID="" -# GITHUB_CLIENT_SECRET="" -# LINKEDIN_CLIENT_ID="" -# LINKEDIN_CLIENT_SECRET="" - -# Custom OAuth Provider (e.g., Authentik) -OAUTH_PROVIDER_NAME="Company SSO" -OAUTH_CLIENT_ID="reactive-resume-client-id" -OAUTH_CLIENT_SECRET="reactive-resume-client-secret" -OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.well-known/openid-configuration" -# OAUTH_SCOPES="openid profile email" # Defaults to these scopes if not set -``` +- Set `APP_URL` to the exact public address people use, with `https` in production. Callback URLs, secure cookies, and trusted origins all come from it. +- Only the `APP_URL` origin (plus `localhost` and `127.0.0.1` on port 3000) is trusted. Other domains pointing at the same installation aren't. +- Behind a reverse proxy, pass the `Host` and `X-Forwarded-Proto` headers through unchanged. ## Troubleshooting - - Your OAuth provider must return an email address for user creation. Ensure: - - The `email` scope is included in your scopes - - Your provider is configured to release the email claim - - The user has an email address set in the identity provider + + The sign-in fails, and the server logs "OAuth Provider provider did not return an email address". Include the `email` scope, make sure your provider releases the email claim, and check that the user has an email address in the provider. - - - The callback URL configured in your OAuth provider must exactly match: ``` - {APP_URL}/api/auth/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port - number differences - Path case sensitivity - - - - Common cause: `APP_URL` does not match the real HTTPS public origin (for example, app is behind TLS but `APP_URL` is - `http://...`). Fix: set `APP_URL` to the canonical HTTPS URL and restart the app. - - - - The custom OAuth option only appears if both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, **and** either: - - `OAUTH_DISCOVERY_URL` is set, **or** - - All three manual URLs are set (`OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, `OAUTH_USER_INFO_URL`) - - Double-check your environment variables and restart the container. - + + The callback registered in your provider must equal `{APP_URL}/api/auth/callback/custom` exactly. Look for a trailing slash, `http` instead of `https`, a different port, or a different hostname. - - - If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct - headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your - domain - - - - Dynamic OAuth client registration allows the app origin and local loopback callbacks by default. Trusted self-hosted - deployments that need arbitrary redirect URIs can enable `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`, which permits any - parseable redirect URI including custom schemes, private hosts, and non-loopback `http://` URLs. Do not enable it on - public or multi-tenant deployments. - - - - The profile mapping depends on your provider returning standard claims: - - `email` (required) - - `name` or `preferred_username` for display name - - `picture`, `image`, or `avatar_url` for avatar - - Check your provider's documentation to ensure these claims are included in the ID token or userinfo response. - + + Failed callbacks open the app's `/auth/error` page. The most common cause is an `APP_URL` that doesn't match the real public address, for example `http://` behind a TLS proxy. Set `APP_URL` to the canonical `https` address and restart. + + + Both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` must be set and non-empty. Restart after changing them. + + + An account with the same email already exists, and its email address isn't verified in Reactive Resume. The person can verify their email (this needs SMTP) and try again. Accounts created through SSO are always treated as verified. + + + If you use `OAUTH_DISCOVERY_URL` and upgraded from before v5.2.8, run the issuer update in [Upgrading an install that uses OIDC discovery](#upgrading-an-install-that-uses-oidc-discovery). + + + This is a different feature: Reactive Resume is then the OAuth server, for tools that connect to it. Dynamic client registration accepts your app's own origin and local loopback callbacks. Trusted private installations that need other redirect URIs can set `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI=true`, which accepts any parseable URI. Don't enable it on public or shared installations. -## Security considerations +## Security checklist - - - Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never - be transmitted over unencrypted connections. - - - Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager. - - - Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations. - - - Keep `AUTH_SECRET` and `BETTER_AUTH_API_KEY` private. Rotating `AUTH_SECRET` may invalidate active sessions. - - - Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume. - - +- Use `https` for both Reactive Resume and your provider. +- Keep `OAUTH_CLIENT_SECRET`, `AUTH_SECRET`, and `BETTER_AUTH_API_KEY` out of version control. Rotating `AUTH_SECRET` signs everyone out. +- Register the exact redirect URI; avoid wildcards. +- Request only the default scopes; Reactive Resume needs nothing more. + +## Related pages + +- [Environment variables](/self-hosting/environment-variables): every variable the server reads. +- [Self-hosting with Docker](/self-hosting/docker): where to put these variables in a container setup. +- [Self-hosting on Vercel](/self-hosting/vercel): adding variables to a Vercel project. diff --git a/docs/self-hosting/upgrading-to-v6.mdx b/docs/self-hosting/upgrading-to-v6.mdx new file mode 100644 index 000000000..3e43471c8 --- /dev/null +++ b/docs/self-hosting/upgrading-to-v6.mdx @@ -0,0 +1,245 @@ +--- +title: "Upgrading to v6" +description: "Upgrade a self-hosted Reactive Resume v5 installation to v6: back up, run the startup migrations, convert old styles, and check API and MCP changes." +--- + +Reactive Resume v6 is a full redesign. For people using it, almost everything looks different; for you as the operator, the upgrade is a normal image or deployment update with a few things to plan for. This page walks through the upgrade and lists what changes in the database, the routes, the API, and the MCP server. For the user-facing changes, see [What's new in v6](/guides/whats-new-in-v6). + +This page is for installations already on v5. Coming from v4? Follow [Migrating from v4](/self-hosting/migration) first. + +## 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. +- **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. +- **Vercel projects must switch to the Services framework preset** before they deploy v6. + +## Before you upgrade + + + + Take a full PostgreSQL backup (for example with `pg_dump`) and back up your uploads: the local storage directory, the S3 bucket, or the Blob store. Check that you can restore the backup. A backup is the simplest way back if anything goes wrong; see [Roll back to v5](#roll-back-to-v5). + + + If scripts, automations, or AI clients use the API or MCP server, read [API changes](#api-changes) and [MCP changes](#mcp-changes). Some fields and tools were removed. + + + Migrations usually finish in seconds, but the letter migration scans every resume. On large installations, allow a few minutes and upgrade when traffic is low. + + + +## Upgrade + + + + Change the image tag to the v6 release (`v6`, a specific version such as `v6.0.0`, or `latest`), then recreate the container: + + ```bash + docker compose pull + docker compose up -d + docker compose logs -f reactive-resume + ``` + + Use your own service name if it isn't `reactive-resume`. Watch the log for `Running database migrations...` followed by `Database migrations completed`. The server starts serving only after that. + + Building from the repository's `compose.yml` instead? See [Self-hosting with Docker](/self-hosting/docker) for the build command. + + + Scale the v5 Deployment to zero, or set the Deployment's update strategy to `Recreate`, so no v5 pod runs next to a v6 pod. Then change the image tag and apply. The first v6 pod takes a database advisory lock and applies the migrations; other pods wait for it. + + See [Self-hosting on Kubernetes](/self-hosting/kubernetes) for the manifests. + + + 1. In the Vercel project, open **Settings → Build and Deployment**, set **Framework Preset** to **Services**, and save. + 2. Sync your fork with the v6 release and redeploy. + + Migrations run in the build step, before the new deployment receives traffic. Don't promote an older v5 deployment afterwards; it can't run against the migrated database. See [Self-hosting on Vercel](/self-hosting/vercel). + + + +## Check the upgrade + +1. Open `/api/health`. `status` should be `healthy` and `version` should start with `6`. +2. Sign in. You land on **Documents**, which lists resumes and cover letters together. +3. If a resume used to contain a cover letter, that letter now appears in **Documents** as a document of its own, named after the resume. +4. If the server log shows `Database schema verification failed`, read [Schema check](#schema-check). + +## Convert styles from the old style editor + +Before Custom Styles became CSS, v5 had a visual style editor that stored its own style rules. v6 no longer renders those rules. Until you convert them, resumes and letters that still use them print with their template's default look. + +The conversion rewrites each affected document's stylesheet as Custom Styles (CSS) that reproduces the old look. It runs only when you start it, it works in four tables (`resume`, `resume_version`, `cover_letter`, `cover_letter_version`), and it changes only the stylesheet: the old rules stay in the data. Files imported later from old JSON exports are converted in the browser as they're imported, so they don't need the script. + +The script is `apps/server/dist/migrate-legacy-styles.mjs`, included in the Docker image. It connects with the server's `DATABASE_URL`. + + + + Converts every affected row in memory and prints counts. Nothing is written. + + ```bash + docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs + ``` + + + Every stylesheet it replaces is appended to the backup file (one JSON object per line) before its row is written. Put the file on persistent storage, such as the `/app/data` volume. + + ```bash + docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs --apply --backup /app/data/legacy-styles-backup.ndjson + ``` + + It's safe to run again, for example after an interruption: converted rows are skipped. You can reuse the same backup file or start a new one. + + + Puts back the stylesheets recorded in the backup file, except on documents whose stylesheet was edited since. + + ```bash + docker compose exec reactive-resume node apps/server/dist/migrate-legacy-styles.mjs --restore /app/data/legacy-styles-backup.ndjson + ``` + + + +Each run ends with a summary per table: + +| Count | Meaning | +| --- | --- | +| `candidates` | Rows that still use the old style rules. | +| `migrated` | Rows converted (or, in a dry run, rows that would be). | +| `changed` | Rows edited while the script ran. They were left alone; run the script again to pick them up. | +| `failed` | Rows whose data couldn't be read. They were left as they are, and the log names each one. | + +On Kubernetes, run the same commands with `kubectl exec deploy/ -- node apps/server/dist/migrate-legacy-styles.mjs ...`. On Vercel there's no container: from a checkout of the same release, put the production variables in the root `.env` (for example with `vercel env pull .env --environment production`), set `DATABASE_URL` to the direct, unpooled connection, run `pnpm install` and `pnpm build --filter=server`, and then run the script with `node`. + +## 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. + +| 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 | + +### 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: + +- 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. +- If a resume held exactly one letter and exactly one application used that resume without a letter, the letter becomes that application's letter. +- Resume version history keeps its older versions as they were. Restoring one saves its letters again as letters (once) instead of putting them back in the resume. + +From then on, any resume that reaches the server with a cover-letter section, through an old browser tab, an imported file, a restored version, or an API client, has the section turned into a saved letter in the same save. A letter with the same text from the same section isn't saved twice. + +### Structured dates + +Dates on entries (experience and its roles, education, projects, volunteering, awards, certifications, and publications) are now stored as structured `dates`: a start and end year or year-month, and whether it's ongoing. No migration changes stored data for this. Instead, every read fills in `dates` from the old text, and every save writes the text (`period` or `date`) from `dates`. Text that couldn't be read exactly, such as "Summer 2016", keeps printing as typed until someone edits the date. The date format is inferred from how the dates were typed. See [Entering dates](/guides/entering-dates). + +### Trash + +Deleting a resume or letter now moves it to Trash. Trashed documents stop being shared publicly, and their public address stays reserved. They're deleted for good, with their files, 30 days later. There's no scheduled job for this: it happens the next time the owner opens their documents, so storage is freed then. See [Using the Trash](/guides/using-the-trash). + +## Removed and redirected routes + +These v5 addresses redirect to their v6 place, so bookmarks and old links keep working through the 6.0 releases. Update any links you control. + +| v5 address | Now goes to | +| --- | --- | +| `/dashboard/resumes` | `/dashboard?type=resume` (tags, sort, and list view carry over) | +| `/dashboard/cover-letters` | `/dashboard?type=letter` | +| `/dashboard/settings/profile`, `/dashboard/settings/authentication` | `/dashboard/settings/account` | +| `/dashboard/settings/integrations`, `/dashboard/settings/api-keys`, `/dashboard/settings/job-search` | `/dashboard/settings/ai` | +| `/agent` | `/dashboard` | +| `/agent/new?resumeId=` | `/builder/?assistant=new` (or `/dashboard` without a resume) | +| `/agent/` | The conversation's document in the editor, with the assistant open. `/dashboard` if the document is gone. | + +Cover letters now open in their own editor at `/builder/letter/`. API, MCP, auth, and public resume addresses (`//`) are unchanged. + +## API changes + +Base paths, authentication, and API keys are unchanged. These changes can affect existing clients. The [API reference](/guides/using-the-api) has the full list. + +**Resume data** +- Dated items carry `dates` (`start`, `end`, `present`, and `raw` for text that couldn't be read). Write `dates`. The text in `period` or `date` is rewritten from `dates` on every save, so a change to the text alone is overwritten. Items sent without `dates` get them from their text. +- `metadata.page.dateFormat` (`short`, `long`, `numeric`, or `iso`) sets how dates print. +- Resumes no longer hold `cover-letter` custom sections. Any you send are saved as letters and removed from the resume. + +**Resumes and documents** +- `DELETE /resumes/{id}` and `DELETE /cover-letters/{id}` now move the document to Trash. New `documents` endpoints list, rename, tag, lock, trash, restore, and permanently delete (`/documents/purge`) resumes and letters together. +- Creating or duplicating a resume no longer needs a `slug`; one is generated from the name. `GET /resumes/{resumeId}/slug-check` validates a slug (lowercase letters and numbers joined by single dashes) and suggests one. +- Version entries have `kind` and `name` instead of `label`. New endpoints get, create (named), rename, and delete resume versions, and letters have the same set under `/cover-letters/{id}/versions`. +- Resume PDF downloads accept only the resume. The `cover-letter` target is gone, and old signed links that ask for it return `404`. Download letters through `/cover-letters`. + +**Cover letters** +- `POST /cover-letters/from-resume` (`copyEmbedded`) is removed, since resumes no longer contain letters. +- Letters gain `layout`, `recipientName`, `recipientCompany`, `letterDate`, `senderLinked`, and `designLinked`, and a `draft` endpoint that drafts the letter body with the user's AI provider. + +**Applications** +- The `rejected` stage and the `archived` flag are gone. Use `status: "closed"` with `closedReason` (`not-selected`, `withdrew`, `accepted-other`, or `no-response`). CSV import still reads `rejected` and `archived` from older exports as closed. +- Listing applications returns closed ones too; `includeArchived` is removed. Filter by `status` instead. +- New interview endpoints (`/applications/{id}/interviews`), and new fields for the linked letter, what was sent (`sentResumeVersionId`, `sentCoverLetterVersionId`, `sentCheckScore`), and `requirements`. +- The account data export now includes applications. + +**Assistant** +- `POST /agent/threads/for-resume`, `POST /agent/threads/{id}/archive`, and `POST /agent/actions/{id}/revert` are removed. Start a conversation with `POST /agent/threads`, passing either `resumeId` or `coverLetterId`. + +## MCP changes + +- Removed: `copy_embedded_cover_letter`. +- `download_resume_pdf` no longer takes a `target`; it always returns the resume. +- `delete_resume` and `delete_cover_letter` now move the document to Trash. +- Added: `add_application_interview` and `update_application_interview`. +- `list_applications` no longer takes `includeArchived`. `update_application` no longer takes `archived`, and accepts `closedReason` with the `closed` stage. +- Resume patch paths document the new `dates` field. The resume schema resource, like `/schema.json`, is now generated from the live schema. + +See [Using the MCP server](/guides/using-the-mcp-server) and [Managing applications with MCP](/guides/managing-applications-with-mcp). + +## Environment variables + +v6 adds no environment variables and removes none. Two behaviors changed: + +- `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. + +See [Environment variables](/self-hosting/environment-variables) for the full list. + +## Schema check + +After the migrations, the server compares the live database with the tables and columns it expects. If something is missing, for example after a partial restore, it logs `Database schema verification failed` and keeps running, which can lead to errors later. + +Set `STRICT_SCHEMA_CHECK=true` to make startup fail instead. That's a good choice for production: a failed start is easier to notice than errors in the middle of the day. To fix drift, restore a consistent backup or recreate the missing objects, then restart. + +## Roll back to v5 + +Restoring the backup you took before the upgrade is the safest way back, and the only one that undoes everything. Stop v6 first, restore the database (and files, if they changed), then start v5. + +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: + + ```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 + ``` + + 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. +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. + + + 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 Discussions](https://github.com/reactive-resume/reactive-resume/discussions) before you try. + + +## Related pages + +- [What's new in v6](/guides/whats-new-in-v6): the user-facing changes. +- [Self-hosting with Docker](/self-hosting/docker): updating and backing up a container installation. +- [Custom styles](/applying-custom-styles): how converted styles look and how to edit them. diff --git a/docs/self-hosting/vercel.mdx b/docs/self-hosting/vercel.mdx index ceb4a155c..e10370539 100644 --- a/docs/self-hosting/vercel.mdx +++ b/docs/self-hosting/vercel.mdx @@ -1,11 +1,26 @@ --- title: "Self-hosting on Vercel" -description: "Deploy Reactive Resume on Vercel Hobby with Neon PostgreSQL, private Vercel Blob, and Upstash Redis." +description: "Deploy Reactive Resume to Vercel as two Vercel Services, with Neon PostgreSQL, private Vercel Blob, and Upstash Redis provisioned by the wizard." --- -This guide deploys Reactive Resume to a Vercel project with the Deploy with Vercel wizard. The wizard provisions a database, file storage, and Redis for you. You supply two secrets. +This guide deploys Reactive Resume to your own Vercel project with the **Deploy with Vercel** wizard. The wizard connects a database, file storage, and Redis for you; you supply two secrets. Use it when you want a managed deployment without running servers. If you'd rather run containers, see [Self-hosting with Docker](/self-hosting/docker). Both run the same application and data format. -The project deploys as two [Vercel Services](https://vercel.com/docs/services): `frontend` serves the static web app from Vercel's CDN, and `backend` runs the shared Hono server in one Node.js 24 Function. Docker is also supported and uses the same API, authentication, templates, and data format. See [Self-hosting with Docker](/self-hosting/docker) for that option. +## How the deployment is laid out + +The repository's `vercel.json` defines two [Vercel Services](https://vercel.com/docs/services) in one project: + +| Service | Source | What it does | +| --- | --- | --- | +| `frontend` | `apps/web` | Serves the built web app (`apps/web/dist`) from Vercel's CDN. | +| `backend` | `apps/server` | Runs the Hono server in one Node.js Function (300-second budget). Its entrypoint, `apps/server/vercel.mjs`, re-exports the adapter the build emits. | + +Top-level rewrites decide which service answers a request. They're checked in order, and the first match wins: + +1. `/api/*`, `/uploads/*`, `/mcp`, `/.well-known/*`, and `/index.html`, `/robots.txt`, `/sitemap.xml`, `/llms.txt`, `/schema.json` go to `backend`. +2. Any other path whose last segment has a file extension (`/assets/app.js`, `/favicon.ico`) goes to `frontend`. +3. Everything else, including every page address such as `/dashboard` or `/alex/resume`, goes to `backend`, which returns the HTML shell with the right metadata. + +Keep the committed `vercel.json` as it is. The rewrites replace an SPA fallback, so don't add one. ## Before you start @@ -18,37 +33,37 @@ You need: openssl rand -hex 32 ``` - Save both values in a password manager. You must keep the same values for the life of the installation. Losing `ENCRYPTION_SECRET` makes saved AI-provider API keys unreadable. + Keep both in a password manager. They must stay the same for the life of the installation: `AUTH_SECRET` signs sessions and tokens, and `ENCRYPTION_SECRET` encrypts the AI provider keys people save. Losing `ENCRYPTION_SECRET` makes those keys unreadable. -The wizard connects three services through Vercel Marketplace: +The wizard connects three Vercel Marketplace services: | Service | Used for | | --- | --- | | Neon PostgreSQL | Application data | -| Vercel Blob (**private**) | Uploaded pictures, files, and agent attachments | -| Upstash Redis | Agent streaming and cancellation, shared rate limits, live resume updates | +| Vercel Blob (**private**) | Pictures, uploaded files, and assistant attachments | +| Upstash Redis | Shared rate limits, live document updates, and assistant replies that survive a reload | -Quotas and permitted use depend on your Vercel, Neon, and Upstash plans. Review [Vercel limits](https://vercel.com/docs/functions/limitations), [Neon](https://vercel.com/marketplace/neon), and [Upstash](https://vercel.com/marketplace/upstash/upstash-kv) before you choose a plan. Turn off automatic paid upgrades if you want to stay inside a free allowance. +Quotas and permitted use depend on your Vercel, Neon, and Upstash plans. Check [Vercel's Function limits](https://vercel.com/docs/functions/limitations), [Neon](https://vercel.com/marketplace/neon), and [Upstash](https://vercel.com/marketplace/upstash/upstash-kv) before you pick a plan, and turn off automatic paid upgrades if you want to stay inside a free allowance. - Each AI agent run stops active work after four minutes. This keeps the run, plus saving and cleanup, inside Vercel Hobby's five-minute Function limit. The limit applies to each question, not to the whole conversation. Docker uses the same limit. + The assistant stops working on a reply after four minutes. This keeps the reply, plus saving and cleanup, inside Vercel Hobby's five-minute Function limit. The limit applies to each message, not to the whole conversation, and Docker uses the same limit. ## Deploy - Click the button: + Select the button: [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Freactive-resume%2Freactive-resume&project-name=reactive-resume&repository-name=reactive-resume&env=AUTH_SECRET%2CENCRYPTION_SECRET&envDescription=Generate+two+independent+secrets+with+openssl+rand+-hex+32.+Keep+these+values+across+deployments.&envLink=https%3A%2F%2Fdocs.rxresu.me%2Fself-hosting%2Fvercel&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22neon%22%2C%22productSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22upstash%22%2C%22productSlug%22%3A%22upstash-kv%22%7D%2C%7B%22type%22%3A%22blob%22%2C%22access%22%3A%22private%22%7D%5D) - On Hobby, select your **personal GitHub account**. Private repositories owned by a GitHub organization require Vercel Pro. + On Hobby, select your **personal GitHub account**. Private repositories owned by a GitHub organization need Vercel Pro. - Approve Neon, Upstash, and Blob. Set Blob access to **private**. Pick nearby regions for all three, ideally close to the Function region (`iad1` by default). + Approve Neon, Upstash, and Blob. Set Blob access to **private**. Pick regions close to each other and to the Function region (`iad1` by default). @@ -56,60 +71,60 @@ Quotas and permitted use depend on your Vercel, Neon, and Upstash plans. Review - Set **Framework Preset** to **Services**. Keep the project root at the repository root and keep the committed `vercel.json`. Do not select the `apps/web` subdirectory and do not add an SPA fallback rewrite. + Set **Framework Preset** to **Services**. Keep the project root at the repository root and keep the committed `vercel.json`. Don't select the `apps/web` subdirectory. - The build compiles both apps and applies database migrations before the deployment goes live. You do not run migrations yourself. + The `backend` build compiles both apps, then runs `apps/server/dist/prepare-deployment.mjs`, which checks the configuration and applies database migrations before the deployment goes live. You don't run migrations yourself. - Do not paste Docker's `.env.example` into Vercel. Its local URLs and S3 settings select the wrong services. + Don't paste Docker's `.env.example` into Vercel. Its local URLs and S3 settings select the wrong services, and the build refuses any storage other than Blob. ## Check the deployment -1. Open `https://.vercel.app/api/health`. `database`, `storage`, and `redis` should all report `healthy`. A sleeping Neon database can fail the first check; retry once. +1. Open `https://.vercel.app/api/health`. `database`, `storage`, and `redis` should each report `"status": "healthy"`, and `version` shows the release you deployed. A sleeping Neon database can fail the first check; retry once. 2. Open the production domain and create an account. -3. Optional: add an AI provider under **Settings** to enable AI features. +3. Optional: to use AI features, each person adds their own provider under **Settings → AI**. 4. Optional: configure SMTP for verification and password-reset emails. Without SMTP, emails are written to the Function logs. -5. Optional: add social or custom OAuth sign-in with the callback URLs in the [SSO guide](/self-hosting/sso). +5. Optional: add Google, GitHub, LinkedIn, or your own identity provider. See [Single sign-on (SSO)](/self-hosting/sso) for the callback URLs. ## Use a custom domain 1. Add the domain to the Vercel project. 2. Set `APP_URL` to the full origin, for example `https://resume.example.com`. -3. Update the callback URLs of every OAuth provider you configured. +3. Update the callback URL of every OAuth provider you configured. 4. Redeploy. -Changing the domain does not move stored files. Files stay under the same `DEPLOYMENT_NAMESPACE`. +Changing the domain doesn't move stored files. They stay under the same `DEPLOYMENT_NAMESPACE`. ## Update the deployment -Redeploy the same project. Keep its connected services and secrets unchanged. +Redeploy the same project, for example by syncing your fork. Keep its connected services and secrets unchanged. + +- Migrations run in the build step, never in the runtime Function. A database advisory lock serializes concurrent deployments. +- Rolling back to an older deployment doesn't roll back the database schema. Restore a database backup, or follow the rollback notes in the release's upgrade guide. - Installations created before Reactive Resume used Vercel Services must switch first. Open **Settings → Build and Deployment**, set **Framework Preset** to **Services**, save, and then redeploy. Vercel builds the `services` configuration only when the project uses this preset. + Projects created before Reactive Resume used Vercel Services (v5.3 and earlier) must switch before they deploy v6. Open **Settings → Build and Deployment**, set **Framework Preset** to **Services**, save, and then redeploy. Vercel builds the `services` configuration only with this preset. Read [Upgrading to v6](/self-hosting/upgrading-to-v6) before you redeploy. -- Migrations run in the build step, never in runtime Functions. A database advisory lock serializes concurrent deployments. -- Rolling back to an older deployment does not roll back the database schema. Keep migrations backward-compatible, or restore a database backup. - ## Preview deployments -Preview builds refuse to run migrations by default, so untrusted preview code cannot change your production database. +Preview builds refuse to run migrations by default, so untrusted preview code can't change your production database. The build fails with "Preview deployment needs an isolated database" until you opt in. To enable previews: 1. Connect separate Neon, Upstash, and Blob resources to the **Preview** environment. 2. Set `ALLOW_PREVIEW_MIGRATIONS=true` for **Preview** only. -A storage namespace does not isolate SQL rows. Never connect the production database to the Preview environment. +A storage namespace doesn't isolate database rows. Never connect the production database to the Preview environment. ## Back up your data Back up the Neon database and the Blob store. Store `AUTH_SECRET` and `ENCRYPTION_SECRET` separately from those backups. -Moving between Docker and Vercel does not copy the database or files. Migrate them yourself. +Moving between Docker and Vercel doesn't copy the database or files. Migrate both yourself. ## Build locally @@ -120,45 +135,51 @@ vercel pull --environment production APP_URL=https://your-project.vercel.app vercel build --prod ``` -Replace sensitive pulled values with local-only ones first. The CLI has no deployment hostname before publishing, so `APP_URL` is required here. Cloud builds set it automatically. +Replace sensitive pulled values with local-only ones first. The CLI has no deployment hostname before publishing, so `APP_URL` is required here; cloud builds set it automatically. -## Environment variables +## Vercel-specific variables -Explicit variables take precedence over the Marketplace aliases listed here. +On Vercel, the server reads the variables that Marketplace integrations inject. A variable you set explicitly always wins over these fallbacks. For everything else (SMTP, OAuth providers, feature flags), see [Environment variables](/self-hosting/environment-variables). -| Variable | Required | Behavior | +| Variable | Required | Behavior on Vercel | | --- | --- | --- | | `AUTH_SECRET` | Yes | Signs sessions and tokens. Keep it constant. | -| `ENCRYPTION_SECRET` | Yes | At least 32 characters. Encrypts saved AI-provider API keys. Keep it constant. | -| `APP_URL` | No | Public origin. Defaults to the production domain in Production and to the deployment domain in Preview. Set it for a custom domain. | +| `ENCRYPTION_SECRET` | Yes | At least 32 characters. Encrypts saved AI provider keys. The build fails without it. | +| `APP_URL` | No | Public origin. Defaults to the production domain (`VERCEL_PROJECT_PRODUCTION_URL`) in Production and to the deployment domain in Preview. Set it for a custom domain. | | `DATABASE_URL` | Injected | Pooled runtime connection. Falls back to `POSTGRES_URL`. | | `DATABASE_MIGRATION_URL` | No | Direct connection for migrations. Falls back to `DATABASE_URL_UNPOOLED`, then `POSTGRES_URL_NON_POOLING`, then `DATABASE_URL`. | | `DATABASE_POOL_MAX` | No | Maximum database connections per Function instance. Default `10`. | -| `REDIS_URL` | Injected | Redis TCP/TLS URL. Falls back to Upstash's `KV_URL`. REST credentials alone do not work. | -| `STORAGE_BACKEND` | No | Must resolve to `blob` on Vercel, which is the default. The build fails with any other value. | +| `REDIS_URL` | Injected | Redis TCP/TLS URL. Falls back to Upstash's `KV_URL`. REST credentials alone don't work. The build fails without Redis. | +| `STORAGE_BACKEND` | No | Resolves to `blob` on Vercel. The build fails with any other value, so remove any `S3_*` variables. | | `BLOB_READ_WRITE_TOKEN` | Injected | Provided by the connected Blob store. `BLOB_STORE_ID` with Vercel OIDC also works. | -| `DEPLOYMENT_NAMESPACE` | No | Prefix for Blob objects and Redis keys. Defaults to `production`, or to a per-branch value in Preview. Keep it constant after you store files. Set different values if two installations share one Blob store or Redis database. | -| `ALLOW_PREVIEW_MIGRATIONS` | No | Set to `true` in Preview only after you connect isolated preview resources. | - -For SMTP, OAuth providers, and feature flags, use the same variables as Docker. See [Self-hosting with Docker](/self-hosting/docker). +| `DEPLOYMENT_NAMESPACE` | No | Prefix for Blob objects and Redis keys. Defaults to `production`, or to the branch URL in Preview. Keep it constant once files exist. Set different values if two installations share one Blob store or Redis database. | +| `ALLOW_PREVIEW_MIGRATIONS` | No | Set to `true` in Preview only, after you connect isolated preview resources. | ## Upload limits -Vercel limits Function request bodies to 4.5 MB. The web app sends larger requests through private Blob staging, so these application limits still apply: +Vercel limits Function request bodies to 4.5 MB. The web app sends larger requests through private Blob staging, so the application's own limits still apply: -- General uploads: 10 MB per file. -- Agent attachments: 25 MiB per file and 100 MiB per thread. +- Uploads (pictures and files): 10 MB per file. +- Assistant attachments: 25 MiB per file and 100 MiB per conversation. -Blob objects are never public. The application serves public pictures and authorizes private files itself. API clients that send large RPC requests must follow the [large RPC requests](/guides/large-rpc-requests) protocol. REST (`/api/openapi`) and MCP request bodies stay subject to the 4.5 MB limit. +Blob objects are never public. The server serves public pictures and checks access to private files itself. API clients that send large RPC requests must follow the [large RPC requests](/guides/large-rpc-requests) protocol. REST (`/api/openapi`) and MCP request bodies stay subject to the 4.5 MB limit. ## Troubleshooting | Symptom | Fix | | --- | --- | -| Deployment does not build as services | Set **Framework Preset** to **Services** under **Settings → Build and Deployment**, then redeploy. | -| First build fails on configuration | Check that all three services are connected to Production and both secrets are set. Remove any `S3_*` variables and any `STORAGE_BACKEND` value other than `blob`. | -| Preview build refuses to migrate | Connect isolated preview resources, then set `ALLOW_PREVIEW_MIGRATIONS=true` for Preview. | -| Large upload returns `413` | Use the web app or the [large RPC requests](/guides/large-rpc-requests) protocol. Vercel Pro does not raise the request body limit. | -| Agent reconnect or stop fails | Check the Upstash TLS URL, the remaining Upstash quota, and that every environment uses the expected `DEPLOYMENT_NAMESPACE`. | -| Sign-in redirects to another hostname | Set `APP_URL` to the domain you use and redeploy. Do not add wildcard trusted origins. | +| The deployment doesn't build as services | Set **Framework Preset** to **Services** under **Settings → Build and Deployment**, then redeploy. | +| The build fails with "Vercel requires private Blob storage" | Connect a private Blob store and remove any `S3_*` variables and any `STORAGE_BACKEND` value other than `blob`. | +| The build fails with "Vercel requires Redis and ENCRYPTION_SECRET" | Connect Upstash Redis to the environment and set `ENCRYPTION_SECRET`. | +| A preview build refuses to migrate | Connect isolated preview resources, then set `ALLOW_PREVIEW_MIGRATIONS=true` for Preview. | +| Pages return 404 or the wrong content | Check that the project root is the repository root and `vercel.json` is unchanged. Don't add an SPA fallback rewrite. | +| A large upload returns `413` | Use the web app or the [large RPC requests](/guides/large-rpc-requests) protocol. Vercel Pro doesn't raise the request body limit. | +| Assistant replies don't resume after a reload, or **Stop** doesn't work | Check the Upstash TLS URL, the remaining Upstash quota, and that every environment uses the expected `DEPLOYMENT_NAMESPACE`. | +| Sign-in redirects to another hostname | Set `APP_URL` to the domain you use and redeploy. | | Files are missing after a configuration change | Restore the original `DEPLOYMENT_NAMESPACE` and Blob connection. | + +## Related pages + +- [Upgrading to v6](/self-hosting/upgrading-to-v6): what changes when an existing installation moves to v6. +- [Single sign-on (SSO)](/self-hosting/sso): sign in through your own identity provider. +- [Environment variables](/self-hosting/environment-variables): every variable the server reads. diff --git a/docs/spec.json b/docs/spec.json index b5d2b7589..f599c22d3 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -1,7 +1,7 @@ { "info": { "title": "Reactive Resume", - "version": "5.3.2", + "version": "6.0.0", "description": "Reactive Resume API", "license": { "name": "MIT", diff --git a/docs/use-cases/ai-resume-builder.mdx b/docs/use-cases/ai-resume-builder.mdx index 8d5972064..35166507a 100644 --- a/docs/use-cases/ai-resume-builder.mdx +++ b/docs/use-cases/ai-resume-builder.mdx @@ -1,40 +1,57 @@ --- title: "AI resume builder" -description: "Use Reactive Resume as an AI resume builder with bring-your-own OpenAI, Anthropic, Gemini, OpenRouter, or Ollama providers for edits and drafts." +description: "Use Reactive Resume as an AI resume builder: connect your own OpenAI, Anthropic, Gemini, Ollama or other provider to draft, review and tailor resumes." --- -Reactive Resume works as an AI-assisted resume builder, but the AI is optional and bring-your-own-provider: you configure the provider, model, endpoint, and API key you want it to use. +Reactive Resume is an AI resume builder that uses your own AI provider. You connect a provider and key in **Settings → AI**, and the assistant, the writing review, cover-letter drafts and file imports all run through it. Everything else in Reactive Resume works without AI. -## What AI does in Reactive Resume +## What AI can do in Reactive Resume -AI can make assisted changes in the builder, review your writing in the ATS checker, produce agent drafts, and support import workflows. The builder and the ATS checker both work without it. +Once a provider is connected, AI shows up in these places: -Use these guides for the full setup: +- **The assistant.** Open it from the editor bar or with ⌘ J (Ctrl J on Windows and Linux). It reads the resume or cover letter you have open and answers in a side panel. When it wants to change something, it proposes an edit that you accept or reject one at a time. You can attach files, and it can ask you a clarifying question before it writes. +- **Writing review in Check.** The **Writing** tab in Check mode sends your resume's text to your provider and comes back with suggested rewrites for weak bullets. +- **Tailoring for a job.** From a saved job application, **Tailor a resume** makes a copy of your resume linked to that job. The assistant and Check then work against its posting, so you can ask for changes aimed at that role. +- **Cover-letter drafts.** In a new cover letter, **Draft from the posting** writes a first draft from the linked job application and your resume. Without an application, **Draft from your resume** drafts from your resume alone. You then edit the draft. +- **Importing files.** With a provider connected, PDF resumes are read by the model for a more faithful import, and Word files can be imported at all. Without a provider, PDFs are read in your browser instead. -- [Using artificial intelligence](/guides/using-ai) -- [Using AI in the builder](/guides/using-ai-in-the-builder) -- [Using the AI Agent workspace](/guides/using-ai-agent) -- [AI Agent tools](/guides/ai-agent-tools) +The resume checks in Check mode (readability score, issues, job match) and the public [ATS checker](/guides/using-the-ats-checker) run without AI. ## Bring your own provider -You configure an AI provider from the Integrations settings: the provider type, the model, a base URL when one is needed, and the API key. +Reactive Resume does not sell AI credits or run a model for you. You choose the provider, the model and the key. **Settings → AI** lists 16 options: OpenAI, Anthropic, Google Gemini, Vercel AI Gateway, OpenRouter, Mistral, Cohere, xAI, Groq, DeepSeek, Together AI, Fireworks AI, Cerebras, Perplexity, Ollama, and any OpenAI-compatible endpoint (such as a model you run yourself). -This is not a hosted AI-writing product. You decide whether to enable AI, which provider to connect, and when to send resume content to it. +That means: -## Where AI fits in the workflow +- Your provider bills you directly for what you use, at its own prices. +- Your resume text goes only to the provider you picked, with your key, and only when you use an AI feature. +- You can switch providers or remove the key at any time. -AI helps most once you already have resume content or structured source material. Review its suggestions, apply changes in the builder, or use agent drafts as separate workspaces before deciding what belongs in the final resume. +## When to use AI + +AI helps most once you have real content to work from: an existing resume, a job posting, or rough notes about your experience. Ask the assistant to tighten a summary, rewrite bullets around results, or check a draft against a posting, then review each proposed edit before you accept it. ## When not to use AI -Do not use AI features if you do not want your resume content sent to the provider you configure. Do not treat AI output as final application material without reviewing it, and use the regular builder when you need exact control over the wording. - -## Next action - -Configure and test a provider with [Using artificial intelligence](/guides/using-ai). Then use [Using AI in the builder](/guides/using-ai-in-the-builder) for in-place help, or [Using the AI Agent workspace](/guides/using-ai-agent) for draft-based work. +Skip the AI features if you do not want your resume content sent to a third-party provider, or if your instance has not configured AI (self-hosted servers need an encryption secret and Redis for saved providers and the assistant). Never send AI output to an employer without reading it: you are responsible for every claim on your resume. - Review AI-generated resume content before using it in applications. You are responsible for the accuracy of the final - resume. + Review every AI suggestion before you accept it. Models can invent details, dates or numbers that are not true. + +## Next steps + + + + Add your provider and key in Settings, then test the connection. + + + Ask for changes and accept or reject each proposed edit. + + + Get a readability score, issues and an AI writing review. + + + Start a tailored copy or a cover letter from a saved application. + + diff --git a/docs/use-cases/api-mcp-resume-automation.mdx b/docs/use-cases/api-mcp-resume-automation.mdx index 2c9b5f6df..c937a3808 100644 --- a/docs/use-cases/api-mcp-resume-automation.mdx +++ b/docs/use-cases/api-mcp-resume-automation.mdx @@ -1,53 +1,59 @@ --- title: "API and MCP resume automation" -description: "Automate resume workflows in Reactive Resume with API keys, the Patch API, the MCP server, AI agent tools, and the JSON resume schema." +description: "Automate resumes, cover letters and job applications in Reactive Resume with API keys, the OpenAPI endpoints, the Patch API and the MCP server." --- -Reactive Resume supports automation through authenticated API access and an MCP server, so compatible tools can work with your resumes and job applications. Agents can list, read, create, import, duplicate, and patch resumes. They can also track applications, move opportunities through stages, add notes and follow-ups, attach sent documents, and run Application Copilot actions. +Reactive Resume has an authenticated API and an MCP server, so scripts, integrations and AI clients such as Claude or Cursor can work with your documents and job applications. Both use the same account data you see in the app. -## Automation options +## What you can automate -Use the API for direct programmatic access from scripts, services, or integrations. Use MCP when you want an AI tool or agent that speaks the Model Context Protocol to work with your resumes through exposed tools. +Through the API and the MCP server, a client acting as you can: -Key docs: +- **Resumes:** list, read, create, import, duplicate, patch, lock, delete, download as PDF, and read sharing statistics. +- **Cover letters:** list, read, create, update, duplicate, export, import and delete. +- **Job applications:** create and update applications, move them through stages, add notes and interviews, edit the activity timeline, attach the documents you sent, bulk-update or delete, and import rows from a spreadsheet. +- **Job-focused actions:** fill in an application from a job posting, score a resume against it, create a tailored resume copy, and draft a message such as a follow-up. -- [Using the API](/guides/using-the-api) -- [Using the patch API](/guides/using-the-patch-api) -- [Using the MCP server](/guides/using-the-mcp-server) -- [AI Agent tools](/guides/ai-agent-tools) -- [JSON Resume schema](/guides/json-resume-schema) +The job-focused actions use your own AI provider, connected in **Settings → AI**. -## Common automation workflows +## API or MCP? -You can build workflows that: +Use the **API** for scripts, services and integrations you write yourself. It is described by an OpenAPI spec, and large requests are supported. -- Create a resume from structured data. -- Import a full resume JSON document. -- Read resume data for review or transformation. -- Patch targeted fields without replacing the whole resume. -- Connect an MCP-compatible client to operate on resumes with authenticated tools. -- Track job applications end to end from an MCP client. -- Import existing application rows from a spreadsheet parser. -- Move applications through stages and log timeline notes. -- Attach the resume or cover-letter PDF sent for an application. -- Score a linked resume against a job description and create a tailored resume copy. -- Draft cover letters and recruiter follow-ups from saved application context. +Use the **MCP server** when you want an AI client that speaks the Model Context Protocol to read and edit your resumes through ready-made tools. The address to paste into your client is shown under **MCP server** in **Settings → AI**. -Start with the Patch API for targeted resume updates. It is the safest option because it is built around explicit changes to existing resume data. +For targeted resume edits, prefer the Patch API (JSON Patch operations) over replacing the whole resume. It changes only the fields you name, so it is less likely to overwrite something by accident. -## Authentication and scope +## Authentication -API requests use API keys. MCP can use OAuth2 in clients that support it, with API keys as a fallback. If you self-host, use your own instance URL for the API and MCP endpoints. +Create an API key in **Settings → AI → API keys**. Keys act as you and can read and edit your documents. You choose an expiry of **30 days**, **90 days** or **Never**, the key is shown once, and you can revoke it at any time. MCP clients that support OAuth can sign in without a key. + +If you self-host, replace `https://rxresu.me` with your own instance's address in every API and MCP example. - Automation changes affect the resumes available to the authenticated account or instance. Test workflows on a copy of a - resume before using them for important application materials. + Automated changes are real changes to your account. Try a new workflow on a duplicate of a resume first, and use + History in the editor to go back if something goes wrong. ## When not to use automation -Do not reach for API or MCP automation when you only need to edit one resume by hand. The builder is faster for one-off changes, template selection, and visual review. Avoid automation for important resume updates unless you can test the exact changes on a copy first. +For editing one resume by hand, choosing a template or checking the layout, the editor is faster. Automation pays off when you repeat the same change across many documents, keep applications in sync with another tool, or want an AI client to do the typing for you. -## Next action +## Next steps -For scripts and integrations, create an API key with [Using the API](/guides/using-the-api), then use [Using the patch API](/guides/using-the-patch-api) for targeted edits. For agent workflows, start with [Using the MCP server](/guides/using-the-mcp-server). + + + Create a key and make your first request. + + + Change specific resume fields with JSON Patch. + + + Connect Claude, Cursor or another MCP client. + + + Track job applications from an AI client. + + + +For the shape of resume data, see the [JSON Resume schema](/guides/json-resume-schema). For big payloads, see [Large RPC requests](/guides/large-rpc-requests). diff --git a/docs/use-cases/export-and-share-resumes.mdx b/docs/use-cases/export-and-share-resumes.mdx index a10c9bd10..aeca69dae 100644 --- a/docs/use-cases/export-and-share-resumes.mdx +++ b/docs/use-cases/export-and-share-resumes.mdx @@ -1,51 +1,53 @@ --- title: "Export and share resumes" -description: "Export Reactive Resume resumes as PDF, DOCX, Markdown, or JSON files and share password-protected public URLs with recruiters and hiring managers." +description: "Download Reactive Resume resumes as PDF, Word, Markdown or JSON, or share a public link with an optional password and view statistics." --- -Reactive Resume exports resumes as PDFs, and it can also give you a public resume URL to send to a person: a recruiter, a hiring manager, a collaborator, or a visitor to your portfolio. Public resume URLs are not search-indexed by default. +Reactive Resume gives you two ways to get a resume in front of someone: download a file, or share a link to a live page. Both live in the **Share** sheet in the editor, and **Download PDF** sits in the editor bar for the most common case. -## Export a PDF +## Download a file -Export a PDF when an application requires a file upload, or when you want a fixed document to send by email. +The **Download** tab in the Share sheet offers four formats: -See [Exporting your resume](/guides/exporting-your-resume) for the export workflow. +| Format | Best for | +| --- | --- | +| **PDF** | Applications and email. Looks exactly like the page you designed. | +| **Word** (.docx) | Portals or recruiters who ask for Word. The layout is simplified. | +| **Markdown** (.md) | Plain text with headings, for pasting into application forms and notes. | +| **JSON** | A complete backup of the resume's data, which you can import back into Reactive Resume. | -## When to export +You can set the file name before downloading. If the resume belongs to a job application that has a cover letter, you can download the letter at the same time. Cover letters download in the same four formats from their own Share sheet. -Export a PDF when a job application requires an uploaded document, when you need a fixed copy for your records, or when you want to send a file that will not change after you submit it. +## Share a public link -## When to share a link +The **Link** tab turns on a public page for the resume at an address such as `rxresu.me/your-username/your-resume`. You choose the last part of the address. From the same tab you can: -Share a public resume URL when the recipient can open a link and you want them to see the current version of your resume. +- Copy the link or open the public page. +- Let visitors download the PDF, or not. +- Require a password before anyone can view it. +- See how many times the page was viewed and the PDF downloaded over the last 30 days. -## Share a public resume URL +Public pages are not listed anywhere and ask search engines not to index them. People find them only through a link you share. Turn the link off and the page stops working immediately. -Public sharing creates a URL you can send to someone else. The page shows the current version of your resume and can offer a download to viewers. +## When to download and when to share -Public resume URLs are meant for people who get the link from you or find it through you. +Download a file when an application asks for an upload, or when you want a fixed copy that will not change. Share a link when the reader can open a URL and should always see your latest version, for example in an email signature, a portfolio or a networking message. -## When not to share only a link +If only certain people should read the resume, add a password. A link on its own is not access control: anyone who has it can open it. -Do not send only a public URL when an application requires a PDF or document upload. Do not use public sharing for a restricted audience unless you turn on password protection, or keep the resume private until you are ready. - -See [Sharing your resume publicly](/guides/sharing-your-resume-publicly) for setup, password protection, statistics, and how to turn public access off. - -## Use the builder dock - -The builder dock has shortcuts for common editing and sharing actions inside the resume builder. - -See [Using the builder dock](/guides/using-the-builder-dock) for the available actions. +## Next steps - - Download a fixed resume file. + + Download PDF, Word, Markdown or JSON, or print. - - Share a live resume URL with selected recipients. + + Set the address, password and download options. + + + Use plain text for forms, notes and AI tools. + + + Create a letter that matches your resume's design. - -## Next action - -Use [Exporting your resume](/guides/exporting-your-resume) if you need a file, or [Sharing your resume publicly](/guides/sharing-your-resume-publicly) if a link will do. diff --git a/docs/use-cases/free-resume-builder.mdx b/docs/use-cases/free-resume-builder.mdx index 07fd91620..410c59d4e 100644 --- a/docs/use-cases/free-resume-builder.mdx +++ b/docs/use-cases/free-resume-builder.mdx @@ -1,62 +1,48 @@ --- title: "Free resume builder" -description: "Reactive Resume is a free resume builder with no paywalls or premium tiers for creating, editing, exporting, and sharing unlimited resumes online." +description: "Reactive Resume is a free resume builder with no premium tier: unlimited resumes and cover letters, 15 templates, and PDF, Word, Markdown and JSON downloads." --- -Reactive Resume is a free resume builder. Write your resume in a browser, pick a template, export a PDF, and share a public URL when you want someone else to read it. Public resume URLs are meant for people you send them to, and they are not search-indexed by default. +Reactive Resume is a free resume builder. On [rxresu.me](https://rxresu.me) you get every feature with no premium tier, no watermark and no paywall on downloads. The project is open source and funded by donations, which is why nothing is held back. -## What you can do for free +## What you get for free -- Create and manage resumes from the dashboard. -- Edit resume content with a live preview in the builder. -- Choose from the templates included with Reactive Resume. -- Export your resume as a PDF. -- Share a public resume URL with recruiters, hiring managers, or collaborators. +- **Unlimited documents.** Keep as many resumes and cover letters as you need, and copy one for each job. +- **15 templates.** Switch at any time without retyping; your content stays the same and only the design changes. +- **Design controls.** Font pairings (or any of about 500 fonts), text size, density, accent colour, margins, A4, Letter or Free-form pages, and multi-page layouts. +- **Every download format.** PDF, Word, Markdown and JSON, as often as you like. +- **Check mode.** A readability score, issues pinned to the exact lines on your page, and keyword matching against a job posting. +- **Public links.** Share a live page with an optional password and see how often it is viewed. +- **Application tracker.** Follow every job from saved to offer, with a board, a calendar for interviews and insights. +- **Version history.** Name versions and go back to any earlier state. +- **Imports.** Bring in a PDF, a Reactive Resume or JSON Resume file, or a LinkedIn data export. Word files work too once you connect an AI provider. +- **50+ languages** for the app interface. -Start with [Introduction](/getting-started) for the product overview, or follow the [Quickstart](/getting-started/quickstart) to create your first resume on [rxresu.me](https://rxresu.me). +AI features are free to use in Reactive Resume too, but they run on your own AI provider account, which may charge you. See [AI resume builder](/use-cases/ai-resume-builder). -## Builder workflow +## When the hosted app is the right choice -Reactive Resume sticks to the core resume workflow: add your profile, work history, education, skills, projects, and other sections, then adjust the layout and template before exporting. +Use [rxresu.me](https://rxresu.me) when you want a resume quickly and do not want to run any software. You need an account so your documents are saved between visits. Sign up with an email address and password, or with a social account where one is offered. -Useful guides: +## When it is not -- [Creating your first resume](/guides/creating-your-first-resume) -- [Choosing a template](/guides/choosing-a-template) -- [Fitting content on a page](/guides/fitting-content-on-a-page) -- [Using the builder dock](/guides/using-the-builder-dock) +If your organization needs its own deployment, its own sign-in provider or control over where data is stored, run your own instance instead. See [Self-hosted resume builder](/use-cases/self-hosted-resume-builder). -## When to use this path +If you only want to check an existing PDF, you do not need an account at all: the [ATS checker](/guides/using-the-ats-checker) runs in your browser. -Use the hosted app when you want a resume quickly and do not want to run any infrastructure. It covers personal resume editing, template selection, PDF export, and sharing a public URL with people who need to read your resume. - -## When not to use this path - -Do not rely on the hosted app alone if your organization requires a controlled deployment, custom auth, or specific data residency rules. Review [Self-hosting with Docker](/self-hosting/docker) instead. If an application requires a file upload, export a PDF rather than sending only a public URL. - -## Export and sharing options - -Download a PDF for applications that need a file. You can also turn on a public resume URL when a link is more convenient than an attachment. - -Public resume URLs are for the people you send the link to. For details, see [Exporting your resume](/guides/exporting-your-resume) and [Sharing your resume publicly](/guides/sharing-your-resume-publicly). - -## Next action - -Open [rxresu.me](https://rxresu.me) to create a resume, or follow [Creating your first resume](/guides/creating-your-first-resume) for the guided workflow. - -## Related resources +## Next steps - - Open the official Reactive Resume instance. + + Sign up, open a sample resume and download your first PDF. - - View the project repository. + + Start blank, from a sample or from an import. - - Review the project license. + + Compare the 15 templates and switch in one click. - - Learn how the hosted service describes data handling. + + Download PDF, Word, Markdown or JSON. diff --git a/docs/use-cases/open-source-resume-builder.mdx b/docs/use-cases/open-source-resume-builder.mdx index b022e5143..72901312f 100644 --- a/docs/use-cases/open-source-resume-builder.mdx +++ b/docs/use-cases/open-source-resume-builder.mdx @@ -1,54 +1,50 @@ --- title: "Open-source resume builder" -description: "Learn how Reactive Resume works as an open-source resume builder with public source code, an MIT license, and self-hosting support." +description: "Reactive Resume is an open-source resume builder under the MIT license: read the code, contribute, translate, or run your own instance." --- -Reactive Resume is an open-source resume builder. The source code is public, the license is MIT, and you can either use the hosted app or run your own instance. +Reactive Resume is an open-source resume builder. Its source code is public on [GitHub](https://github.com/reactive-resume/reactive-resume) under the [MIT license](/legal/license). You can use the hosted app at [rxresu.me](https://rxresu.me), read exactly how it handles your data, or run your own copy. ## What open source means here -The project repository is on [GitHub](https://github.com/reactive-resume/reactive-resume). You can read the code, report issues, contribute improvements, and see how the builder, the API, the self-hosting setup, and the documentation are put together. +- **Everything is in the open.** The editor, the PDF renderer, the API, the MCP server and these docs all live in one public repository. +- **The MIT license is permissive.** You can use, modify and host the software, including for an organization, as long as you keep the license notice. +- **The hosted app runs the same code.** There is no closed "pro" edition. Features on [rxresu.me](https://rxresu.me) are the features in the repository. +- **Your data is portable.** Download any resume as JSON, or export all your documents and applications as a zip from **Settings → Account**. A resume's JSON file imports into any other Reactive Resume instance. -The license is documented in [License](/legal/license). +## What the software includes -## Product capabilities +Reactive Resume v6 is more than a resume editor. It covers the whole job search: -Reactive Resume has a browser-based builder, resume templates, PDF export, public sharing links, API access, MCP support, and optional AI-assisted workflows. Public resume URLs are meant for the people you share them with and are not search-indexed by default. The main workflow starts in the hosted app at [rxresu.me](https://rxresu.me) or on your own deployment. - -Helpful starting points: - -- [Introduction](/getting-started) -- [Quickstart](/getting-started/quickstart) -- [Creating your first resume](/guides/creating-your-first-resume) -- [Managing resumes from the dashboard](/guides/managing-resumes-from-the-dashboard) +- Resumes and cover letters as separate documents, with 15 templates and shared design. +- Check mode for readability, issues and job-posting keywords, plus a public ATS checker. +- Public links with passwords and view statistics. +- An application tracker with board, list, calendar and insights views. +- An optional AI assistant that uses your own provider. +- An API and an MCP server for automation. +- An interface translated into more than 50 languages by volunteers. ## When to use this path -Use the open-source path when you want to see how resume data and exports are handled, contribute fixes or translations, or keep the option of running your own deployment. +Start from the source if you want to audit how resume data is stored and exported, fix a bug, add a translation, or run the app yourself. If you only want to write a resume, use [rxresu.me](https://rxresu.me) and follow the [Quickstart](/getting-started/quickstart) instead. -## When not to use this path +Open source does not make a deployment compliant on its own. If you run an instance, you are responsible for its security, storage and privacy. -Do not start with the source code if you only want to build a resume. Use [rxresu.me](https://rxresu.me) or the [Quickstart](/getting-started/quickstart) first. Do not assume open source covers your compliance needs on its own; if you run an instance, read the self-hosting and privacy docs. - -## Contribution and customization - -To contribute or to understand the codebase, start with the contributor docs. They cover the local development setup, the package layout, and the project conventions. +## Contribute - Set up the repository locally. + Run the repository locally. - Understand the app and package boundaries. + Learn how the apps and packages fit together. - Help translate the app. + Help translate the interface into your language. - Browse issues, pull requests, and source code. + Browse issues, pull requests and source code. -## Next action - -To contribute, set up the project with [Development setup](/contributing/development). To run it yourself, start with [Self-hosting with Docker](/self-hosting/docker). +To run your own instance, start with [Self-hosting with Docker](/self-hosting/docker). diff --git a/docs/use-cases/privacy-focused-resume-builder.mdx b/docs/use-cases/privacy-focused-resume-builder.mdx index 1909d5fbb..892093bee 100644 --- a/docs/use-cases/privacy-focused-resume-builder.mdx +++ b/docs/use-cases/privacy-focused-resume-builder.mdx @@ -1,46 +1,51 @@ --- title: "Privacy-focused resume builder" -description: "Reactive Resume's privacy-focused design: open-source transparency, self-hosting, public sharing controls, and optional bring-your-own AI providers." +description: "How Reactive Resume protects your resume data: private by default, password-protected links, in-browser ATS checks, your own AI provider, and self-hosting." --- -Reactive Resume is a privacy-focused resume builder: the code is open source, you can self-host it, editing is private by default, and both sharing and AI are choices you make yourself. +Reactive Resume is a privacy-focused resume builder. Your documents are private until you choose to share them, AI only runs when you connect your own provider, and the code is open source, so you can check these claims yourself or run your own instance. -## Privacy controls in the resume workflow +## Private by default -You edit resumes inside your account. When you want someone else to read one, turn on a public URL and send them the link. Public resume URLs are not search-indexed by default. You can turn public access off again, and a public resume can require a password. +- **Documents stay in your account.** Nobody else can see your resumes or cover letters. A resume becomes visible only when you turn on its public link in **Share → Link**. Cover letters have no public link. +- **Public links are unlisted.** Public pages are not listed anywhere and ask search engines not to index them. +- **Links can require a password.** Add one in the Link tab, and turn the link off at any time to stop access immediately. +- **Private notes stay private.** Notes you keep on a resume never appear on the page, in the PDF or on the public link. Hidden sections and entries are removed from the public link too. -For the sharing workflow, see [Sharing your resume publicly](/guides/sharing-your-resume-publicly). For account security, see [Setting up two-factor authentication](/guides/setting-up-two-factor-authentication) and [Setting up passkeys](/guides/setting-up-passkeys). +## Checks that never leave your browser -## When to use this path +The public [ATS checker](/guides/using-the-ats-checker) reads your PDF in your browser. The file is not uploaded and you do not need an account. In the editor, Check mode's score, issues and job match also run without sending your resume to an AI service. -Use this path when you want to understand the tradeoffs before choosing hosted use, self-hosting, public sharing, API automation, or AI features. It matters most if your resume holds sensitive job-search information, or if you plan to share links with only a few people. +## AI only on your terms -## When not to use this path +Reactive Resume does not include a built-in AI model. AI features run only after you connect a provider of your choice in **Settings → AI**, using your own key, and your text goes only to that provider when you use an AI feature. Without a provider, PDF imports are read in your browser. Remove the key at any time to turn AI off. -Do not treat public sharing as access control. Use password protection, or keep the resume private, if only specific people should open the link. Do not turn on AI features unless you are comfortable sending the prompts and resume content to the provider you configure. +## Account security and your data -## Open source and self-hosting +Protect your account with [two-factor authentication](/guides/setting-up-two-factor-authentication) or [passkeys](/guides/setting-up-passkeys). You can [export all your data](/guides/exporting-your-data) as a zip at any time, and deleting your account permanently removes your documents and applications. -The source code is on [GitHub](https://github.com/reactive-resume/reactive-resume), so you can read how the application works. If you need direct control over infrastructure, storage, auth providers, and deployment policy, run your own instance. +## Full control with self-hosting -Start with: - -- [Self-hosting with Docker](/self-hosting/docker) -- [Self-hosting examples](/self-hosting/examples) -- [Single sign-on](/self-hosting/sso) -- [Privacy Policy](/legal/privacy-policy) - -## Optional AI features - -AI is optional. Reactive Resume does not need it to create, edit, export, or share a resume. If you do want AI help, you configure the provider and the key yourself. - -For setup details, see [Using artificial intelligence](/guides/using-ai), [Using AI in the builder](/guides/using-ai-in-the-builder), and [Using the AI Agent workspace](/guides/using-ai-agent). +If you need to decide where data is stored, who can sign in and which services it talks to, run your own instance. The source is on [GitHub](https://github.com/reactive-resume/reactive-resume), and self-hosted instances can use your own database, file storage, email server and single sign-on provider. - Review the privacy policy for the instance you use. If you self-host, you are responsible for the deployment's data - handling, storage, email, and third-party provider configuration. + The hosted service at rxresu.me is covered by its [Privacy Policy](/legal/privacy-policy). If you self-host, you are + responsible for how your deployment stores and handles data. -## Next action +## Next steps -Read the [Privacy Policy](/legal/privacy-policy), then go to [Sharing your resume publicly](/guides/sharing-your-resume-publicly) for link controls or [Self-hosting with Docker](/self-hosting/docker) for infrastructure control. + + + Control the address, password and downloads. + + + See what is sent to your provider and when. + + + Run Reactive Resume on your own infrastructure. + + + Use your organization's identity provider. + + diff --git a/docs/use-cases/self-hosted-resume-builder.mdx b/docs/use-cases/self-hosted-resume-builder.mdx index 9285afebe..4b2e29d2f 100644 --- a/docs/use-cases/self-hosted-resume-builder.mdx +++ b/docs/use-cases/self-hosted-resume-builder.mdx @@ -1,40 +1,52 @@ --- title: "Self-hosted resume builder" -description: "Run Reactive Resume as a self-hosted resume builder with Docker Compose, PostgreSQL, optional object storage, Single Sign-On, and v4 to v5 migration." +description: "Run Reactive Resume on your own server with Docker, Kubernetes or Vercel: PostgreSQL, local or S3 storage, single sign-on, and the full v6 feature set." --- -You can run Reactive Resume on your own infrastructure instead of using the hosted instance. +You can run Reactive Resume on your own infrastructure instead of using [rxresu.me](https://rxresu.me). A self-hosted instance has the same features as the hosted app: resumes, cover letters, Check mode, public links, the application tracker, the AI assistant, the API and the MCP server. ## When self-hosting is a good fit -Self-hosting helps when you need control over the deployment, domain, database, storage, email delivery, authentication providers, and operational policy. The Docker guide is the main setup path for most deployments. +Self-host when you need to control where data lives, who can sign in, and which outside services the app talks to. Common reasons are a company or school that wants its own resume tool, strict data-residency rules, or a personal server you already run. -Start here: +If you only need a resume for yourself, the hosted app is less work. -- [Self-hosting with Docker](/self-hosting/docker) -- [Self-hosting examples](/self-hosting/examples) -- [Single sign-on](/self-hosting/sso) -- [Migration guide](/self-hosting/migration) +## What a deployment needs -## Core deployment pieces +The production image runs a single Node.js process on port 3000 that serves the app, the API and the MCP server. Around it you need: -A typical self-hosted deployment uses: +- **PostgreSQL** for accounts, documents and applications. Database migrations run automatically when the server starts. +- **File storage** for photos and uploads: a local folder, any S3-compatible service, or Vercel Blob. +- **SMTP** for account emails. Without it, emails are written to the server log, which is fine for testing. +- **Optional: Redis and an encryption secret.** Both are needed for saved AI providers and the assistant. Core resume features work without them. +- **Optional: single sign-on** through Google, GitHub, LinkedIn or a custom OpenID Connect provider. -- Reactive Resume application container. -- PostgreSQL database. -- SMTP configuration for account emails, or console-logged emails in simple development setups. -- Optional S3-compatible storage for uploads. -- Optional SSO or custom OAuth configuration. +Rendering PDFs needs no extra service: there is no headless browser to run. -The Docker guide has the environment variable reference and a Compose example. +## Ways to deploy -## Feature considerations + + + The main path: Docker Compose with PostgreSQL. + + + Run the same image in a cluster. + + + Deploy the frontend and backend as Vercel Services. + + + Ready-made setups with reverse proxies and storage. + + -Some features need extra configuration. Saved AI providers need server-side encryption configured, and the AI Agent workspace uses Redis. Self-hosted deployments can also expose API and MCP endpoints from their own domain. +Every setting is listed in [Environment variables](/self-hosting/environment-variables). For sign-in providers, see [Single sign-on](/self-hosting/sso). -For automation setup on a self-hosted instance, see [Using the API](/guides/using-the-api) and [Using the MCP server](/guides/using-the-mcp-server). +## Upgrading an existing instance + +Already running v5? Read [Upgrading to v6](/self-hosting/upgrading-to-v6) before you pull the new image. It covers what changes in the database, the API and the MCP server. For older v4 instances, see the [Migration guide](/self-hosting/migration). - Replace hosted URLs such as https://rxresu.me with your own instance URL when following API, MCP, or - sharing examples. + When you follow API, MCP or sharing examples in these docs, replace `https://rxresu.me` with your own instance's + address.