Customise
Customise your experience
Everything adjustable lives on one Settings page — the clock, the stamps, each module's rules and editors — plus a handful of environment variables for the instance itself.
On this page
/settings is the one place every rule and every editor lives. The play pages stay clean and
carry a ⚙ that jumps to the right section. Each section here has a 📖 back to this page.
Clock
- Quick-adjust buttons — the four ± buttons on the dashboard, in minutes, comma-separated.
Default
15,60,360,1440. Any count works; four fit the row best. - Undo window — how far back undo reaches, in minutes. Default 15;
0means no limit. Stamped photos quote the clock as it was, which is why the default is short.
Stamps
- Stamped photo layout —
auto(by aspect ratio), orbarHairline ·heroCollar ·badgeTag ·dossierCase File ·railStrip. See Photos. - Stamped photo: stats to overlay — an ordered list of token ids. Default
streak_current,tasks_verified,session_id,session_task. - Case File grid — the tokens the
dossierlayout lays out in its grid. - Session recap card — the tokens in the recap card's grid. Default
base_seconds,adjust_seconds,frozen_seconds,sess_tasks,sess_spins,sess_photos. - Share card — the grid on a right now card. Default
time_locked_d,streak_current,level,tasks_verified,total_locked,sessions_n. - Share card, the last stretch — the grid on a week card. Every id here is from the
windowgroup, because a grid mixing "3 locks this week" with "41 locks ever" is a grid nobody can read once the labels are abbreviated to fit. Defaultwin_sessions,win_tasks,win_spins,win_reflections,win_photos,win_xp. - Share card, one badge — the grid on a milestone card, resolved as the numbers stood
when the badge was earned. Default
total_locked,streak_current,level,sessions_n,tasks_verified,longest_lock. - Share card: theme — which palette a share card starts in:
midnight(the app's own colours),steel,moss,sapphire,amethyst,gold,plate. See Themes.
These are the starting lists. The Share page can add, remove and reorder stats for one card without touching any of them — the choice lives in the link.
A token that has no value at that moment (a session token on a photo taken while unlocked, a
rate on a one-day-old log, a window token on a card that has no window) is simply dropped, so
a list can be generous.
Token reference
| Group | Token | Prints |
|---|---|---|
| moment | date_long · time_hm · datetime_full · weekday |
when the photo was taken |
| moment | status |
LOCKED / FROZEN / TIME SERVED / UNLOCKED at that moment |
| session | time_locked · time_locked_d |
elapsed in the lock (short / with days) |
| session | day_n · day_n_cal |
day N of the lock (by 24 h / by calendar day) |
| session | time_remaining · pct_served |
to go · percent served |
| session | base_seconds |
the sentence — hidden while a hidden lock runs |
| session | adjust_seconds · frozen_seconds |
net adjustments · time frozen |
| session | session_id · session_task |
lock number · the task this photo verified |
| session | sess_tasks · sess_spins · sess_photos · sess_xp |
counts inside this lock |
| lifetime | total_locked · sessions_n · longest_lock |
lifetime locked · locks · longest |
| lifetime | tasks_verified · tasks_failed |
training done / failed |
| lifetime | reflections_n · checkins_n · spins_n · photos_n |
counts |
| lifetime | time_added · time_removed |
totals |
| streak | streak_current · streak_best |
days |
| rate | tasks_per_week · task_fails_per_week · spins_per_week · locks_per_week |
per week |
| rate | reflect_days_per_week · checkin_days_per_week · reflect_compliance |
days per week · % of locked days reflected |
| rate | locked_hours_per_week · locked_pct_28d |
hours per week · % of the last 28 days |
| game | level · rank · xp |
as of that moment |
| window | win_days · win_locked · win_locked_pct |
the span · locked in it · % of it |
| window | win_active_days · win_sessions |
days with something logged · locks begun |
| window | win_tasks · win_task_fails · win_spins |
counts inside the span |
| window | win_photos · win_reflections · win_checkins · win_badges |
counts inside the span |
| window | win_xp · win_added · win_removed |
xp earned · time added · time removed |
Every value is computed as of the photo's timestamp — see the overlay is as-of the shutter.
The window group is the exception, and the only one that reads two moments: it counts
what happened between them. Those tokens only mean anything on a week share card, so
everywhere else they say nothing and are left out — the same treatment as a stat that has no
value yet, rather than a zero that would claim a quiet week.
Activities
An activity is a thing you want to count — reflections, sits, walkies, anything. Latch ships with a starter set and you can add your own at the bottom of Settings.
The important part: an activity selects events you already have. It is a lens over your log, not a second log. Add one today and it is filled in from your whole history immediately — and removing one deletes nothing, because it never owned any of those events in the first place.
Each activity has:
- icon and colour — the colour is what the Calendar draws. Two activities should not share one, or a day's colours stop telling you what you did.
- slug — lower-case, no spaces. It becomes an event kind and the tail of five stat ids, so it is the one field worth thinking about before you save.
- match module / match kind — which events belong to this activity. Leave module blank for any; put several kinds in, comma-separated, to fold them together. Both blank matches nothing, deliberately — a blank activity that matched everything would quietly claim your entire log.
- streak — what a run is judged against:
| cadence | a run continues while |
|---|---|
|
daily| you do it every day. Today is grace — an empty today does not break it | |weekly_n| every week carries target separate days or more. The current week is grace | |every_n_days| you never leave a gap bigger than target days | |none| this has no streak, and none is shown | That last row is not a cop-out. A wheel-spin counter has no run to break, so showing it a0would be claiming one was broken. - time from — where "time doing this" is read:
entry(headspace),media(recordings),trance(sits). Blank means this has no duration, and none is shown rather than0s. - one-tap button — gives it a button on the dashboard that logs it, which it then counts.
- xp — awarded for a one-tap log.
Tracking something Latch already does? Put that module's kind in match kind and untick
one-tap button. Tracking something of your own? Leave match module as activity, set
match kind to your slug, and tick one-tap button.
The five stats every activity gets
Saving an activity adds five ids to the token lists above, with no restart:
| id | prints | absent when |
|---|---|---|
since_<slug> |
how long since the last one | it has never happened — never 0s, which would read as just now |
days_<slug> |
days you have done it | — (a genuine 0 is printed) |
streak_<slug> |
the current run | its cadence is none |
total_<slug> |
time spent doing it | it declares no time from |
count_<slug> |
how many altogether | — |
So a new activity is shareable the moment it exists: put streak_walkies in the share-card list
and it appears. The full list of ids your instance has right now is at the bottom of the
Activities section in Settings.
Handler
The name, the reflection minimums, the check-in cadence, the missed-day penalty, and the whole prompt library. Full detail on The Handler. Three presets, if you want a starting point:
| Gentle | Firm (default) | Strict | |
|---|---|---|---|
| Minimum reflection | 60 chars | 120 | 300 |
| Check-in every | 8 h | 4 h | 2 h |
| Missed-reflection penalty | 0 min | 30 | 120 |
| Minimum spoken reflection | 15 s | 30 | 60 |
Then rewrite the prompts in the voice you want. The seeded lines are firm but clean; the library is where the Handler's personality lives.
Wheel
The cooldown (minutes; 0 while testing) and the segment table — label, weight, effect,
value, colour, enabled. See Wheel of Fortune.
Training
The default deadline for a task that has none (hours, default 24) and the task library — rewards, penalties, XP, photo required, per-task deadline, enabled. See Training.
Display name and other instance settings
These are environment variables on the container, not fields on the page:
| Variable | Meaning | Default |
|---|---|---|
LATCH_APP_NAME |
the name in the header, the tab title, the footer | Latch |
LATCH_TZ |
the timezone every date is shown in and every when is typed in (events are stored as UTC epochs) | America/Detroit |
LATCH_API_TOKEN |
the bearer token for /api/v1. Empty means the API is off. |
(empty) |
LATCH_TICK_SECONDS |
how often the scheduler sweeps freezes and deadlines | 30 |
LATCH_DATA_DIR |
where the database and media live | /data in the container |
LATCH_DOCS_DIR |
where this guide's markdown lives | /app/docs in the container |
Set them in .env next to docker-compose.yml and recreate the container. Renaming the app
is a one-line change: LATCH_APP_NAME=Kennel.
Themes
There is one look — dark, game-like, no build pipeline. app/latch/web/static/style.css is a
single file with CSS variables at the top (--accent, --accent2, --cyan, --bg) if you
want to fork the palette. A theme system and a curated gallery look are on the
roadmap.
This page also ships inside the app, at /guide — so your own instance always serves the guide for the version you are running, with the internet unplugged. Get Latch · Something wrong here? Tell me.