Cauce by example
Each example shows the file and the graph it describes, side by side. They add one thing at a time: if you understand the first, the last one has nothing new in it except more lines.
They are all valid files. You can paste them into a .toml and run them
through reply2fb cauce validar exactly as they are.
The keys stay in Spanish —cuentas, flujos, condicion— because that is
what the parser reads. A translated key is a file that does not validate, so
here only the explanations are in English.
1. The smallest thing that works
One source, one destination, and a flow between them.
formato_version = 1credenciales = "ausentes"
[[cuentas]]handle = "gss"plataforma = "instagram"rol = "lectura"id_remoto = "17841400000000000"
[[cuentas]]handle = "sumud"plataforma = "mastodon"rol = "escritura"instancia_url = "https://mastodon.cl"max_por_hora = 4
[[flujos]]clave = "gss-a-sumud"origen = "gss"destino = "sumud"activa = trueauto_publica = falseThat describes this:
formato_version and credenciales are mandatory and come first. The first
is what gets looked at before trying to understand the rest; the second says
whether this is a backup or a description — and whether somebody archives a file
believing they can restore from it depends on that.
max_por_hora goes on the RECEIVING account, not on the flow. The limit
belongs to whoever receives: if tomorrow three flows point at sumud, the
ceiling is still set by Mastodon. On the flow, three routes “of 4 per hour”
would do 12.
2. With a filter: recent videos only
One line is added to the flow.
[[flujos]]clave = "gss-a-sumud"origen = "gss"destino = "sumud"activa = trueauto_publica = falsecondicion = { tipo = ["VIDEO"], tag = ["palestina"], desde_dias = 30 }The three criteria are met AT THE SAME TIME. “Videos about Palestine from the last 30 days” wants all three things. With an OR, this flow would fire on far more than its author asked for.
And it is fail-closed: an item with no tags does not pass a tag filter. We do not know what it is about, so we do not claim that it is about that.
desde_dias bounds what is EMITTED, not what is archived. The full history
still enters the archive — the dotted arrow in the diagram. What this window
prevents is a two-year rescue turning into two years of publications.
3. A subscription to a public account
An account that gave permission but cannot hand over an API token. It is read by opening its profile with a browser.
[[cuentas]]handle = "colectivo"plataforma = "instagram"rol = "lectura"id_remoto = "colectivo"via = "navegador"poll_horas = 12via is explicit and is not inferred from a missing credential. Inferring
it would turn a revoked token into scraping a public profile, without
anybody deciding so. And without this field in the file, exporting and
re-importing turned the subscription into a normal account: it requires a
token, it does not have one, and it brings back nothing — silently.
4. An RSS feed
The feed URL goes in id_remoto. It carries no instancia_url, which names
the server of a federated account.
[[cuentas]]handle = "sitio-propio"plataforma = "rss"rol = "lectura"id_remoto = "https://ejemplo.org/feed.xml"poll_horas = 1
[[flujos]]clave = "rss-a-telegram"origen = "sitio-propio"destino = "canal"activa = trueauto_publica = falsecondicion = { autor = ["Redacción"] }autor exists for the sources that distinguish author from account: the
feed of a site where many people write, or a Telegram channel with bylines. On
an Instagram account, author and account are the same thing and the criterion
is useless.
5. Your own site as a destination
A web account is a site: it is read from and published to.
[[cuentas]]handle = "sitio"plataforma = "web"rol = "ambos"id_remoto = "sitio-propio"
[cuentas.sitio]api_url = "http://127.0.0.1:8181"sitio_url = "https://ejemplo.org"uid_servicio = "160451ec-828f-4a5b-b78c-04ff5c44fcab"tipos = ["post", "articulo"]api_url and sitio_url are different on purpose. The first is how this
process reaches the site, and it is usually an internal address that does not
exist for anybody else. Publishing a link to 127.0.0.1 is publishing a broken
link.
It goes on the account and not in the process environment. It used to be a
global variable, which meant one installation served one site: with two web
accounts, both published to whichever the variable named — the second one to
the wrong site, with no error at all.
6. A complete installation
Everything together: two sources, three destinations, four flows.
formato_version = 1credenciales = "ausentes"
# ── Sources ──────────────────────────────────────────────────────────────[[cuentas]]handle = "gss"plataforma = "instagram"rol = "lectura"id_remoto = "17841400000000000"poll_horas = 6consentimiento_vigente = "mastodon+telegram"
[[cuentas]]handle = "prensa"plataforma = "rss"rol = "lectura"id_remoto = "https://ejemplo.org/feed.xml"poll_horas = 1
# ── Destinations ─────────────────────────────────────────────────────────[[cuentas]]handle = "sumud"plataforma = "mastodon"rol = "escritura"instancia_url = "https://mastodon.cl"max_por_hora = 4max_en_vuelo = 1video_como_imagenes = 3
[[cuentas]]handle = "canal"plataforma = "telegram"rol = "escritura"id_remoto = "-1001234567890"
# ── Flows ────────────────────────────────────────────────────────────────[[flujos]]clave = "gss-a-sumud"origen = "gss"destino = "sumud"nombre = "recent videos → Mastodon"activa = trueauto_publica = falsecondicion = { tipo = ["VIDEO"], desde_dias = 30 }
[[flujos]]clave = "gss-a-canal"origen = "gss"destino = "canal"activa = trueauto_publica = false
[[flujos]]clave = "prensa-a-sumud"origen = "prensa"destino = "sumud"activa = falseauto_publica = falsecondicion = { autor = ["Redacción"] }Notice that prensa-a-sumud is dotted: it is declared with activa = false. The graph draws what exists, not what runs.
7. A backup, which is a different thing
The same format, with a different header and two extra blocks. It only comes
out through the CLI, with cauce exportar --respaldo.
formato_version = 1credenciales = "cifradas"
[cifrado]algoritmo = "AES-256-GCM"huella_clave = "b3f1a9c7"
[[cuentas]]handle = "gss"plataforma = "instagram"rol = "lectura"id_remoto = "17841400000000000"credencial_cifrada = "k3JhbmRvbS1ieXRlcy1hcXVpLXF1ZS1uby1zaXJ2ZW4..."This file does not get committed. It carries the credentials exactly as they
are in the database. huella_clave is there so you can know, before attempting
a restore, whether this installation has the key they were encrypted with —
finding that out at the end of a disaster is too late.
Over HTTP the tokens always come out empty. There is no parameter and no permission that makes the panel hand this over. It is what leaves the invariant “no endpoint returns ciphertext” without exceptions to remember.
What the plan says
Before applying anything, reply2fb cauce importar archivo.toml shows what it
would do (the output is in Spanish, as the CLI is):
+ prensa = gss = sumud ~ canal (max_por_hora) + prensa-a-sumud = gss-a-sumud · 'gss' trae anotado un consentimiento: es informativo y el import NO lo otorga. Un permiso se afirma en el panel, con quién lo afirma y cuándo · gss-a-sumud: viene activo. El import lo crea PAUSADO igualcuentas: 1 nueva(s), 2 sin cambios, 1 a actualizar · flujos: 1 nuevo(s), 1 sin cambios
Esto es sólo el PLAN: no se escribió nada. Con --aplicar se aplica.| Mark | Means |
|---|---|
+ | Would be created |
= | Already there, and left as it is |
~ | It is there, and the file declares something else — which fields are listed |
✗ | Conflict or failure: the whole plan refuses |
· | A warning: something the file declares and the import will not apply |
~ lists the fields. “The account was updated” says nothing;
“(max_por_hora)” reveals that what changes is the tap, which is a decision with
consequences for how much goes out per hour.