Say how long the edge access log really lives, and fix README defaults
Read on the box 2026-10-07 (read-only): ship-logs.timer at 02:40 UTC copies the edge log, with wp-flamenet, wp-shepherd and enigma-bbs, to b2enc:logs, an rclone crypt remote on Backblaze B2, and deletes archive files older than 90 days. edge-accesslog-prune.timer at 03:30 UTC overwrites the live file in place and removes rotations, and refuses to run unless a ship succeeded in the last 26 hours. prune-edge-logs.timer hourly removes rotations past the first. The archive held 2026-08-31 onward. The relay container's own log is not shipped and at FN_LOG_LEVEL=notice held no client address. /metadata (en, es) said the edge log is "erased weekly, at most seven days". It now says about a day on the machine, longer only if the archive copy fails, and 90 days in the encrypted archive, and that the relay's own log holds no addresses. Same correction for the line about request sizes. PLAINTEXT.md and IDENTIFYING-DATA.md record the reading. The intro row on /metadata now says one pass, not three. README's limits table said FN_BUNDLE_RATE / FN_BUNDLE_RATE_TARGET 120 / 6 and FN_SEND_RATE 600; the code defaults are 600 / 60 and 3000. relay.leaf regenerated from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
78b0a576d5
commit
a55af454fc
@@ -43,8 +43,8 @@ value, and a relay that accepts unsigned tokens is worse than one that is down.
|
||||
| `FN_WORKDIR` | inferred | Directory containing `Resources/` and `Public/`. Set it in a unit file. |
|
||||
| `FN_CLOCK_SKEW` | `60` | Seconds of tolerance on a proof timestamp. |
|
||||
| `FN_FEDERATION` | `closed` | `closed`, `allowlist` or `open`. Closed is a complete relay, not a limited one, and is what a self-hoster gets without configuring anything. An unrecognised value falls back to `closed` rather than refusing to boot, a typo in an optional variable should not stop a relay serving its own users. |
|
||||
| `FN_BUNDLE_RATE` / `FN_BUNDLE_RATE_TARGET` | `120` / `6` | Bundle fetches per hour, per requester and per pair. |
|
||||
| `FN_SEND_RATE` | `600` | Envelopes per hour. |
|
||||
| `FN_BUNDLE_RATE` / `FN_BUNDLE_RATE_TARGET` | `600` / `60` | Bundle fetches per hour, per requester and per pair. |
|
||||
| `FN_SEND_RATE` | `3000` | Envelopes per hour. |
|
||||
| `FN_DELIVERED_RETENTION_DAYS` | `1` | The backstop for a collected envelope the app never confirms. |
|
||||
| `FN_DELETE_ON_ACK` | on | Delete an envelope when the app's next poll confirms it. Postgres only; `0` turns it off. |
|
||||
| `FN_UNDELIVERED_RETENTION_DAYS` | `30` | Must stay below the client's retired-SPK retention (45 days). |
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<tr><td>Cuenta y dispositivo del remitente</td><td>Solo en envíos identificados; un envío <strong>sellado</strong> no guarda ninguno de los dos</td><td>#if(retention.deleteOnAck):en cuanto tu aplicación confirma la recepción, o #(retention.deliveredDays) día(s) después de entregarse si nunca la confirma#else:#(retention.deliveredDays) día(s) después de entregarse#endif</td></tr>
|
||||
<tr><td>El texto cifrado</td><td>Es el mensaje; el servidor no tiene ninguna clave que lo abra</td><td>#(retention.undeliveredDays) días si nunca se recoge</td></tr>
|
||||
<tr><td>Adjuntos (fotos, notas de voz, historias, todo cifrado)</td><td>Los dispositivos los descargan aparte del sobre que los referencia</td><td>#(retention.attachmentDays) días</td></tr>
|
||||
<tr><td>Presentaciones</td><td>Cuando alguien que todavía no tiene forma de escribirte te busca por tu nombre de usuario, o abre uno de tus enlaces de contacto, el servidor le da tres pases, cada uno válido para un mensaje sellado a ti. Guarda el hash de cada pase y a qué cuenta da acceso. No anota quién preguntó, desde dónde ni cuándo. Las aplicaciones todavía no lo usan.</td><td>Cuando se usa el pase, o 10 minutos después de crearlo</td></tr>
|
||||
<tr><td>Presentaciones</td><td>Cuando alguien que todavía no tiene forma de escribirte te busca por tu nombre de usuario, o abre uno de tus enlaces de contacto, el servidor le da un pase válido para un mensaje sellado a ti. Guarda el hash del pase y a qué cuenta da acceso. No anota quién preguntó, desde dónde ni cuándo. Las aplicaciones todavía no lo usan.</td><td>Cuando se usa el pase, o 10 minutos después de crearlo</td></tr>
|
||||
<tr><td><strong>“Este es urgente”</strong>, una marca en el sobre, <strong>no se guarda</strong></td><td>Una llamada tiene que sonar mientras todavía está sonando, así que el remitente le dice al servidor que envíe la notificación con prioridad alta en vez de esperar a que la aplicación consulte. En la práctica, eso significa que el servidor distingue una llamada de un mensaje: no quién llama, ni sobre qué, pero sí que este es urgente. Es lo más informativo que un remitente decide revelar, y es una decisión: nada obliga a poner la marca.</td><td>Nunca se escribe. Se lee para decidir la notificación y se va con la petición.</td></tr>
|
||||
<tr><td><strong>“Este no necesita atención”</strong>, la misma forma, <strong>no se guarda</strong></td><td>La otra cara de la misma moneda: el remitente dice que un sobre es mantenimiento y no algo por lo que interrumpir a una persona, así que el servidor envía un despertar en segundo plano en vez de una alerta. Eso le dice más o menos lo mismo que la marca de arriba, al revés. La alternativa, no enviar nada y dejar que la aplicación decida tras descifrar, significa depender de notificaciones silenciosas que iOS limita, así que el trato honesto es una pequeña revelación a cambio de notificaciones que sí llegan.</td><td>Nunca se escribe, igual que arriba.</td></tr>
|
||||
<tr><td>Contadores del límite de registros (por IP)</td><td>Las cuentas nuevas están limitadas por dirección y por hora, y un contador necesita la dirección</td><td>una hora; el contador solo vive su ventana</td></tr>
|
||||
@@ -173,8 +173,14 @@
|
||||
<p>
|
||||
Lo que está delante del servidor guarda su propio registro de accesos. Eso lo configura
|
||||
el operador, y en un servidor que tú mismo ejecutas, el operador eres tú. En el servidor
|
||||
que funciona en este dominio, el registro de accesos del proxy de entrada se borra cada
|
||||
semana: las direcciones de conexión en la puerta viven como mucho siete días.
|
||||
que funciona en este dominio, el proxy de entrada escribe una línea por cada petición:
|
||||
la dirección de la conexión, la ruta, el tamaño y la hora. En la máquina esas líneas se
|
||||
sobrescriben cada día a las 03:30 UTC, una vez que la copia de ese día ha llegado a un
|
||||
archivo fuera del servidor; si la copia falla, se quedan hasta que una salga bien. La
|
||||
copia del archivo está cifrada y se guarda 90 días, para que una intrusión que se
|
||||
descubra semanas después todavía se pueda rastrear. Así que una dirección en la puerta
|
||||
vive un día, más o menos, en la máquina y 90 días en el archivo. El registro propio del
|
||||
servidor es aparte, no se archiva y no contiene direcciones.
|
||||
</p>
|
||||
<p>
|
||||
Ahora algunas preguntas se pueden hacer sin iniciar sesión, con la cuenta por la que
|
||||
@@ -190,7 +196,7 @@
|
||||
claro que el registro <strong>sobrevive al sobre</strong>: el sobre se borra
|
||||
#if(retention.deleteOnAck):en cuanto tu aplicación confirma la recepción#else:a los
|
||||
#(retention.deliveredDays) día(s)#endif, y la línea que anota cuánto medía queda en el
|
||||
registro de accesos hasta una semana. Rellenar los mensajes en el dispositivo que
|
||||
registro de accesos un día, más o menos, en la máquina y 90 días en el archivo. Rellenar los mensajes en el dispositivo que
|
||||
envía es lo que ocultaría ambas cosas, porque las dos miden los mismos bytes. Eso
|
||||
todavía no está hecho. Un operador que no quiera ninguna de las dos puede apagar el
|
||||
registro de accesos; aquí se mantiene porque sin él no hay forma de ver si el sitio es
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<tr><td>Sender account and device</td><td>Identified sends only, a <strong>sealed</strong> send stores neither</td><td>#if(retention.deleteOnAck):the moment your app confirms receipt, or #(retention.deliveredDays) day(s) after delivery if it never does#else:#(retention.deliveredDays) day(s) once delivered#endif</td></tr>
|
||||
<tr><td>The ciphertext</td><td>It is the message; the server holds no key that opens it</td><td>#(retention.undeliveredDays) days if never collected</td></tr>
|
||||
<tr><td>Attachment blobs (photos, voice notes, stories, all ciphertext)</td><td>Devices fetch them separately from the envelope that references them</td><td>#(retention.attachmentDays) days</td></tr>
|
||||
<tr><td>Introductions</td><td>When somebody with no way to reach you yet looks you up by your username, or opens one of your contact links, the server hands them three passes, each good for one sealed message to you. It keeps the hash of each pass and which account it opens. It does not record who asked, from where, or when. The apps do not use this yet.</td><td>When the pass is used, or 10 minutes after it was made</td></tr>
|
||||
<tr><td>Introductions</td><td>When somebody with no way to reach you yet looks you up by your username, or opens one of your contact links, the server hands them a pass good for one sealed message to you. It keeps the hash of the pass and which account it opens. It does not record who asked, from where, or when. The apps do not use this yet.</td><td>When the pass is used, or 10 minutes after it was made</td></tr>
|
||||
<tr><td><strong>“This one is urgent”</strong>, a flag on the envelope, <strong>not stored</strong></td><td>A call has to ring while it is still ringing, so the sender tells the server to push at high priority instead of waiting for the app to poll. In practice that means the server can tell a call from a message, not who is calling, or about what, but that this one is time-critical. It is the single most informative thing a sender chooses to reveal, and it is a choice: nothing forces the flag on.</td><td>Not stored as a value. But it leaves a trace, and saying “never written down” was wrong: every row in <strong>Ring attempts</strong> below is written only for an urgent envelope, so a row existing for your device is the flag. That is 7 days, with the device and the time, and no content.</td></tr>
|
||||
<tr><td><strong>“This one needs no attention”</strong>, the same shape, <strong>not stored</strong></td><td>The other side of the same coin: the sender says an envelope is housekeeping rather than something a person should be interrupted for, so the server sends a background wake instead of an alert. That tells it roughly as much as the flag above does, inverted. The alternative, sending nothing and letting the app decide after it decrypts, means relying on silent pushes that iOS throttles, so the honest trade is a small disclosure for notifications that actually arrive.</td><td>Never written down, and unlike the flag above this one really does leave no trace: it selects the kind of push and nothing records that it was set.</td></tr>
|
||||
<tr><td>Sign-up throttle counters (per IP)</td><td>New accounts are limited per address per hour, and a counter needs the address</td><td>one hour, the counter lives only for its window</td></tr>
|
||||
@@ -169,8 +169,13 @@
|
||||
<p>
|
||||
Whatever sits in front of the server keeps its own access log. That is the operator's
|
||||
to configure, and on a server you run, the operator is you. On the server operated at
|
||||
this domain, the edge proxy's access log is erased weekly, connection addresses at
|
||||
the front door live for at most seven days.
|
||||
this domain, the edge proxy writes a line for every request: the connection's
|
||||
address, the path, the size and the time. On the machine those lines are overwritten
|
||||
every day at 03:30 UTC, once that day's copy has reached an off-site archive; if the
|
||||
copy fails, they stay until one succeeds. The archive copy is encrypted and kept for
|
||||
90 days, so that an intrusion noticed weeks later can still be traced. So an address
|
||||
at the front door lives about a day on the machine and 90 days in the archive. The
|
||||
server's own log is separate, is not archived, and holds no addresses.
|
||||
</p>
|
||||
<p>
|
||||
Some questions can now be asked without signing in, with the account being asked
|
||||
@@ -185,8 +190,8 @@
|
||||
disclosure as the row above arriving by a second door, and it is worth saying plainly
|
||||
that the log <strong>outlives the envelope</strong>: the envelope is erased
|
||||
#if(retention.deleteOnAck):the moment your app confirms receipt#else:after
|
||||
#(retention.deliveredDays) day(s)#endif, and the line recording how large it was sits
|
||||
in the access log for up to a week. Padding messages on the sending device is what
|
||||
#(retention.deliveredDays) day(s)#endif, and the line recording how large it was stays
|
||||
in the access log for about a day on the machine and 90 days in the archive. Padding messages on the sending device is what
|
||||
would hide both, because both are measuring the same bytes. That has not been done
|
||||
yet. An operator who wants neither can turn the access log off; this one is kept
|
||||
because without it there is no way to see whether the site is reachable at all.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- GENERATED by Scripts/build-doc-pages.sh from README.md.
|
||||
Edits made HERE are deleted the next time that script runs.
|
||||
Change the markdown source and re-run it.
|
||||
source-sha256: 232f2e1b2f514155 -->
|
||||
source-sha256: d24ba7ffde0aae1c -->
|
||||
<article class="band doc doc-generated">
|
||||
<div class="wrap wrap-narrow">
|
||||
<h1 id="flamenet-relay">flamenet-relay</h1>
|
||||
@@ -91,12 +91,12 @@ variable should not stop a relay serving its own users.</td>
|
||||
<tr>
|
||||
<td><code>FN_BUNDLE_RATE</code> /
|
||||
<code>FN_BUNDLE_RATE_TARGET</code></td>
|
||||
<td><code>120</code> / <code>6</code></td>
|
||||
<td><code>600</code> / <code>60</code></td>
|
||||
<td>Bundle fetches per hour, per requester and per pair.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>FN_SEND_RATE</code></td>
|
||||
<td><code>600</code></td>
|
||||
<td><code>3000</code></td>
|
||||
<td>Envelopes per hour.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -37,6 +37,13 @@ still seen and usually matches the same device's authenticated poll.
|
||||
The limiter keys for the new meters, and for the sealed send's recipient
|
||||
buckets, hold a keyed hash of the account instead of its id.
|
||||
|
||||
How long that log lives, read on the box 2026-10-07 (it had drifted from what
|
||||
`/metadata` said): overwritten on the machine daily at 03:30 UTC after the
|
||||
02:40 UTC copy to an encrypted B2 archive succeeds, and kept in that archive
|
||||
90 days. `/metadata` said "erased weekly, at most seven days"; it now says
|
||||
about a day on the machine and 90 days in the archive. Details in
|
||||
`PLAINTEXT.md`.
|
||||
|
||||
---
|
||||
|
||||
## Pass 1, 2026-09-07
|
||||
|
||||
@@ -122,6 +122,19 @@ Now pruned **daily** (`edge-accesslog-prune.timer`), so the window is a day
|
||||
rather than a week. That is the highest-value change available here, and it is
|
||||
the only one that is actually safe, see below.
|
||||
|
||||
That day is the window **on the machine**. Read on the box 2026-10-07:
|
||||
`ship-logs.timer` (02:40 UTC) copies the edge log (with `wp-flamenet`,
|
||||
`wp-shepherd` and `enigma-bbs`) to the `b2enc:logs` remote, an rclone crypt
|
||||
remote on Backblaze B2, and deletes archive files older than 90 days
|
||||
(`--min-age 2160h`). `edge-accesslog-prune.timer` (03:30 UTC) then overwrites
|
||||
the live file in place and removes rotated ones, and refuses to run unless the
|
||||
last ship succeeded within 26 hours, so a failing archive leaves the log on the
|
||||
box rather than losing it. `prune-edge-logs.timer` hourly removes rotations past
|
||||
the first. So a client address lives about a day on the box and 90 days in the
|
||||
archive; the archive held 2026-08-31 onward that day. The relay container's own
|
||||
log is not shipped, and at `FN_LOG_LEVEL=notice` held no client address (one
|
||||
line with an IPv4, its own bind address).
|
||||
|
||||
## Why the obvious fix is wrong
|
||||
|
||||
Traefik can drop `ClientHost` from the access log, which would remove every IP
|
||||
|
||||
Reference in New Issue
Block a user