{% extends "base.html" %} {% import "macros.html" as m %} {% block title %}Settings{% endblock %} {% macro status(done, yes='done', no='pending') %}{{ yes if done else no }}{% endmacro %} {% macro step_errors(step) %}{% set errs = form_errors.get(step, {}) %}{% if errs %}
{% endif %}{% endmacro %} {% macro how(href) %}How it works{% endmacro %} {#- "or describe it": the same change, built by your own agent (Make it yours). -#} {% macro describe(section) %}or describe it{% endmacro %} {#- The page is five tabs. Each lists the section ids it holds, so a link or a redirect to a section (/settings#bcc) opens the tab it is on. -#} {% set groups = [ ('general', 'General', ['make-it-yours', 'you', 'enrichment', 'appearance', 'support', 'about']), ('mail', 'Email & calendar', ['bcc', 'calendar']), ('crm', 'Your CRM', ['fields', 'messaging', 'task-types', 'outcomes']), ('backup', 'Backup', ['backup']), ('advanced', 'Advanced', ['schedule', 'access', 'mcp', 'doctor']), ] %} {% set lb = local_backup %} {% set attention = { 'general': not state.you, 'mail': inbox_alert, 'backup': lb.level != 'ok', } %} {% macro group_start(key) %}{% for gid, label, sections in groups if gid == key %}
{% endfor %}{% endmacro %} {% block content %}

Settings

{% if setup_pending %}

A few steps are still open below. Skip for now

{% endif %} {# ================================================================= General #} {{ group_start('general') }}

Make it yours with your AI

{{ how('/help/adjust') }}

Describe a change, such as a field, a dashboard, a new look or a routine, and your own AI agent builds it in your data folder. Every change is one commit you can undo.

{% if handoff.how == 'copy' %}You get a prompt to paste into your agent; name the agent under AI below for a one-click button.{% else %}Requests go to {{ handoff.label }}; change that under AI below.{% endif %}

You

{{ status(state.you) }}
{% if state.you %}

{{ you.name }}

Sends mail from {{ you.addresses }}

{% else %}

Your name signs the drafts; mail from your addresses counts as outbound.

{% endif %} {{ step_errors('you') }}
{{ 'Change name or addresses' if state.you else 'Your name and addresses' }}

AI: Enrich and Ask the Hermit

{%- if enrich_status.available %}In use: {{ enrich_status.provider }} {%- else %}Unavailable{% endif %}{{ how('/help/settings#ai-enrich-and-ask-the-hermit') }}

Fills a record's empty fields and answers questions about your CRM, using an AI command-line tool you already have.

{% if not enrich_status.available %}

Unavailable: {{ enrich_status.reason }}

Started by launchd or systemd (hermitcrm schedule install --serve), Hermit CRM has a bare PATH: put the CLI in /usr/local/bin, /opt/homebrew/bin or ~/.local/bin, or give its full path as the command below.

{% endif %} {{ step_errors('enrichment') }}
Account
Model
{% if tier_models.strong %}

Answers made on the medium model offer "Retry with {{ model_label(tier_models.strong) }}".

{% endif %}
Command, timeout and model ids

Only for a CLI that names a model differently; an id set here is used on any account. A subscription is not entitled to every API model id, so on one Hermit CRM asks for no particular model where that matters.

Make it yours hands requests to

The agent that builds what you describe under Make it yours, in {{ handoff.folder_shown }}. Claude Code and Cursor open with one click; Codex and Gemini CLI get a command to paste in a terminal; with none you copy the prompt into any agent.

Appearance

{{ step_errors('appearance') }}
Night mode {% for t, label in [('light', 'Off'), ('dark', 'On'), ('system', 'Follow the system')] %} {% endfor %}
Your own look: {{ 'in use' if theme_file.active else 'not in use' }} {% if theme_file.active %}

In use: {{ theme_file.path }}. It changes colours and fonts on top of the default. Delete the file to go back to the default look.

{% if theme_file.problems %}
    {% for line, problem in theme_file.problems %}
  • Line {{ line }}: {{ problem }}
  • {% endfor %}
{% endif %} {% else %}

Not in use. To change colours or fonts, ask your AI tool to write {{ theme_file.path }}, for example "make my Hermit CRM blue". It is one block like this; Help > Settings lists what you can change.

{{ theme_example }}
{% endif %}

Support Hermit

Hermit CRM is free and open source, and stays that way. If it earns its place in your week, you can support its development with a tip on Ko-fi.

Support Hermit on Ko-fi

Opens {{ support_url | replace('https://', '') }} in your browser. Nothing in the app depends on it, and Hermit CRM sends nothing to Ko-fi.

About

VersionHermit CRM v{{ about.version }}{% if update_notice and update_notice.available %} — v{{ update_notice.available }} available: {{ update_notice.hint }}{% elif about.update_note %} ({{ about.update_note }}){% endif %}
Data folder{{ about.data_dir }}
Data format{% if about.data_format is none %}unknown{% else %}{{ about.data_format }}{% if about.data_format != about.latest_format %} (this Hermit CRM writes {{ about.latest_format }}; run hermitcrm migrate){% endif %}{% endif %}
HelpAll help topics, or hermitcrm help
{# ======================================================== Email & calendar #} {{ group_start('mail') }}

BCC capture

{{ status(state.bcc) }}{{ how('/help/settings#bcc-capture') }}
{% if state.bcc and bcc_form.address %}

BCC {{ bcc_form.address }} when you email a prospect, or forward a thread to it, and the mail is logged on the right company.

{% else %}

Put a tracking address in BCC when you email a prospect, and the mail is logged on the right company. With Gmail, you+crm@gmail.com reaches your own inbox, so no second account is needed.

{% endif %}
{% if last_run %} {% if last_run.ok %}

Last import {{ last_run.at | replace('T', ' ') }}: {{ last_run.summary }}

{% else %}

Last import {{ last_run.at | replace('T', ' ') }} failed: {{ last_run.error }}

{% endif %} {% else %}

No import has run yet.

{% endif %} {% if inbox_alert and last_run and last_run.ok %}

{{ inbox_alert }}

{% endif %}

Mail it cannot place waits on Home, under To file.

{% if gmail_filter %}
{% endif %}
{{ step_errors('bcc') }}
{{ 'Change mailbox' if state.bcc else 'Set up the mailbox' }}
{% for key, p in mail_providers.items() %}

{{ p.label }}: {{ p.note }}{% if p.password_url %} Create the app password.{% endif %}

{% endfor %}
{% if os_store %}{% endif %}

{% if os_store %}Secrets are stored in .secrets.toml (mode 600) or {{ os_store_label }}, never in config.toml.{% if os_store == 'secret-service' %} A keyring is locked while you are logged out, so a daily import that runs then cannot read the password; keep it in .secrets.toml if the machine imports without you logged in.{% endif %}{% elif is_windows %}Secrets are stored in .secrets.toml, never in config.toml. They are plain text, readable only by your user account and never committed to git.{% else %}Secrets are stored in .secrets.toml, never in config.toml. No system keyring was found (install libsecret-tools and run a Secret Service such as GNOME Keyring or KWallet to use one), so they are plain text, readable only by your user account (mode 600) and never committed to git.{% endif %}

{% if gmail_filter %}

Gmail filter

Keeps these copies out of your inbox. In Gmail: Settings → Filters → Create new filter.

{{ gmail_filter }}
{% endif %}

Calendar

{% if state.calendar %}{{ status(true) }}{% endif %}{{ how('/help/calendar') }}

{% if not state.calendar %}Optional. {% endif %}Logs past meetings with people at your companies.

{% if calendar_last_run %} {% if calendar_last_run.ok %}

Last meeting import {{ calendar_last_run.at | replace('T', ' ') }}: {{ calendar_last_run.summary }}

{% else %}

Last meeting import {{ calendar_last_run.at | replace('T', ' ') }} failed: {{ calendar_last_run.error }}

{% endif %} {% else %}

No meeting import has run yet. Past meetings whose attendees match no company wait on Home, under To file.

{% endif %}
{{ step_errors('calendar') }}
{{ 'Change calendar address' if state.calendar else 'Add your calendar' }}

Google: Settings → your calendar → Integrate calendar → Secret address in iCal format. Treat it like a password.

{# ================================================================ Your CRM #} {{ group_start('crm') }}

Fields

{{ how('/help/settings#fields') }}{{ describe('fields') }}

Fields of your own, on top of the ones Hermit CRM has. Their values live in your records' front matter; fields.toml describes them.

{{ step_errors('fields') }} {% if custom_field_defs %}
{% for f in custom_field_defs %} {% endfor %}
keylabeltypeonshown in
{{ f.key }}{{ f.label }}{{ f.type }} {{ f.applies_to }}{{ f.show_in | join(', ') }}
{% else %}

No fields of your own yet.

{% endif %}
Add a field
Shown in {% for view in ['detail', 'board', 'companies', 'contacts', 'messages'] %} {% endfor %}
Edit fields.toml directly

This is the file itself. Removing a field stops Hermit CRM showing it; the values stay in your records, and putting the field back brings them back.

Drafts

{{ how('/help/messages') }}{{ describe('messaging') }}

Outreach drafts can use any field of your own as {{ '{' }}key{{ '}' }}. These two also fill the sentences about team size; with neither set, those sentences leave a line for you to fill in.

{{ step_errors('messaging') }}

Task types

{{ describe('task-types') }}

Your own labels for tasks and next steps, used as filters. Renaming one renames it on every task; deleting one leaves it on its tasks, in grey.

{{ step_errors('task-types') }}
{% for t in task_types_form.rows %} {% endfor %}
{{ m.type_chip(t.name) }} {% if not loop.first %}{% endif %} {% if not loop.last %}{% endif %}

Outcomes

{{ how('/help/settings#outcomes') }}{{ describe('outcomes') }}

One per line, in order. The first is what a detected reply counts as; the last is what silence past the message window counts as.

{{ step_errors('outcomes') }}
{# ================================================================== Backup #} {{ group_start('backup') }}

Backup

{{ status(state.backup) }}How to roll back
{{ step_errors('backup') }}

On this computer

{%- if lb.level == 'ok' and lb.age_minutes is not none -%} Last backup {{ 'just now' if lb.age_minutes < 1 else lb.age_minutes ~ ' min ago' }} ({{ lb.size }}). {%- elif not lb.last_ok -%} No backup yet. {%- else -%} {{ lb.summary }} {%- endif -%} {% if lb.last_ok and not schedule.backup_installed %} It does not repeat on its own yet.{% endif %}

Every few minutes, to a repository outside this folder that can only grow, so any version can be put back.

{% if schedule.backup_elsewhere %}

This computer's backup job is set up for another folder, {{ schedule.backup_elsewhere }}; there is one per computer. To back up this folder instead, run hermitcrm --data {{ about.data_dir }} schedule install; that folder is then no longer backed up.

{% elif not schedule.backup_installed or not lb.last_ok %}
Makes a first backup now, then one every 5 minutes in the background.
{% endif %}
Where it is kept

In {{ lb.dest }}, outside your CRM folder, so deleting the folder does not delete the backup. To keep it somewhere else, such as another disk or a synced folder, set backup_dir in config.toml.

{% if lb.lines[1:] %}{% endif %}

Online copy (optional){% if state.remote %} {{ status(true) }}{% endif %}

{% if remote_url %}

Pushed to {{ remote_url }} after every change.

{% else %}

The backup above lives on the same disk as your data. For a copy that survives a lost laptop, push the folder to a git remote after every change.

{% endif %} {% if remote_warning %}

{{ remote_warning }}

{% else %}

The repository must be PRIVATE on GitHub, GitLab or Bitbucket: it holds your contacts and mail.

{% endif %}
{{ 'Change the repository' if remote_url else 'Set up an online copy' }}
{# ================================================================ Advanced #} {{ group_start('advanced') }}

These are set up from a terminal. This tab shows what is running and the commands to copy.

Schedule

{{ status(schedule.installed) }}{{ how('/help/settings#schedule') }}

A daily job runs hermitcrm sync --apply: the BCC import, then the calendar import.

What is installed, and the install command

Daily at 07:00; --at HH:MM changes the time, and without --serve the web app is left to you.

{{ schedule.install_command }}

Access from your phone

{% if access.on_network %}{{ status(true, 'on') }}{% endif %}{{ how('/help/settings#access') }}
{% set no_password %}

Hermit CRM has no password. Anyone who can reach that port can read and write your CRM, so use it on a network you trust, or a private one (Tailscale, WireGuard) rather than public Wi-Fi.

{% endset %} {% if access.on_network %}

The app answers on your network: open {{ access.lan_url }} on a phone on the same Wi-Fi.

{{ no_password }} {% else %}

The app is bound to {{ access.host }}, which only this machine can reach.

How to turn it on
{{ access.serve_command }}

Then open {{ access.lan_url }} on the phone. To make it permanent, set host = "0.0.0.0" in config.toml.

{{ no_password }}
{% endif %}

Access from an AI app (MCP)

{{ how('/help/ai-agents') }}

Lets Claude Desktop, ChatGPT desktop or Cursor on this computer read and write this CRM: your pipeline, the replies you owe, a brief before a meeting, and logging the call afterwards. It can also make Hermit yours (fields, dashboards, routines, the look), each change one commit you can undo.

The config to paste

Your AI app starts it for you: paste this into its MCP config. It talks over stdin and stdout, so the app must run on this computer.

{{ access.mcp_config }}

The command on its own: {{ access.mcp_command }}

Check the installation

hermitcrm --data {{ about.data_dir }} doctor

Add --online to try the IMAP login as well.

{% endblock %}