πΎ Activities π
A thing you want to count. It selects events you already have β
so a new one fills in from your whole log straight away, and removing one deletes nothing.
Each gets five stats you can put on a share card: since_, days_,
streak_, total_, count_ + its slug.
{% macro act_form(a, is_new) %}
{% if a %}
{% endif %}
{% endmacro %}
{% for a in acts_all %}{{ act_form(a, false) }}{% endfor %}
{{ act_form(none, true) }}
Your own thing? Leave match module as
activity and match kind as your slug, tick one-tap button, and it
gets a button on the dashboard that it then counts.
Counting something a module already does? Put that module's kind in
match kind and untick one-tap button.
streak Β· daily every day Β·
weekly_n counts whole weeks carrying target or more Β·
every_n_days allows a gap of target days Β·
none means this has no streak, and none is shown β a counter has no run to
break, so a 0 there would claim one was.
time from is where βtime doing thisβ is read: entry (headspace),
media (recordings), trance (sits). Blank means this has no duration and
none is shown.
every stat id you can put in a share / stamp / recap token list ({{ token_ref|length }})
Paste any of these, comma-separated, into the token fields under
Stamps & recap. Unknown ids are ignored, and a stat with nothing to
say at that moment is left out rather than printed as a zero.
{% for t in token_ref %}{{ t }} {% endfor %}
{% for m, p in panels %}
- API
- {% if api_enabled %}enabled bearer token Β·
/api/v1{% else %}owner token disabled set LATCH_API_TOKEN{% endif %}{% if not hosted %} Β· {% if bot_live %}{{ bot_live }} bot token{{ '' if bot_live == 1 else 's' }} live on Consent{% else %}a chat bot gets its own token{% endif %}{% endif %}
- data
{{ data_dir }}
- timezone
- {{ tz }}
- modules
- {% for m in modules %}{{ m.icon }} {{ m.name }} {% endfor %}
- version
- {{ version }}
{#- π΄ The Host guard (core/guards.py): the names a request may arrive under. A page on
any other name β a DNS-rebinding page included β is refused before it can read a
byte, which is what keeps /export/bundle.zip from being fetched by a stranger's
site that resolved its own name to this box. A name is never learnt from a request
(over plain http nothing tells the owner's typed address from a rebinding page's
fetch): it is given here, or in LATCH_ALLOWED_HOSTS. -#}
{% if not hosts.hosted %}- answers to
-
{%- if hosts.env %}{% for h in hosts.env %}
{{ h }}{{ ', ' if not loop.last }}{% endfor %} (LATCH_ALLOWED_HOSTS)
{%- elif hosts.bad %}no usable name (LATCH_ALLOWED_HOSTS)
{%- elif hosts.pin %}{{ hosts.pin }} β the name given here
{%- else %}no name yet β only its IP addresses and localhost reach it{% endif %}
Β· IP addresses Β· localhost
{%- if hosts.bad %}
β οΈ ignored β these LATCH_ALLOWED_HOSTS entries can never match (write a bare name like latch.home.example or *.home.example, no scheme, no slash): {% for h in hosts.bad %}{{ h }}{{ ', ' if not loop.last }}{% endfor %}{% endif %}
{%- if not hosts.env and not hosts.bad %}
{%- if hosts.pin %}
{% endif %}{% endif %} {% endif %}
{% set eg = modules|selectattr('egress')|list %}
The core makes no outbound calls.
{%- if eg %} Modules that say they do: {% for m in eg %}{{ m.title }}{{ ', ' if not loop.last }}{% endfor %} β each one says what it fetches in its section above.
{%- else %} No module here declares any.{% endif %}
Stamped photos, share cards and recaps are re-encoded with no EXIF; the full backup keeps
your original photos exactly as you uploaded them, EXIF included.
How that is enforced β
Export your data β
{% endblock %}