Cauce
Cauce —Spanish for watercourse, the channel a river runs through— is a TOML file describing where material may flow: which accounts exist and what goes from which to which. It publishes nothing — that is still the worker’s job, with its tap and its guards.
It is good for four things, in the order in which they matter: backup and disaster recovery, bulk sign-up, versioning the configuration in git, and replicating to another installation.
One format, two artefacts
The encrypted backup only comes out through the CLI, and the restore only goes in through the CLI. Over HTTP the tokens always come out empty, and there is no endpoint that accepts them. That cut is what leaves the invariant “no endpoint of this service returns or accepts ciphertext” without exceptions to remember.
The header cannot lie. If the file declares itself ausentes and carries a
credential, or declares itself cifradas and does not carry the [cifrado]
block, validation rejects it. This is not a warning: whether somebody
archives a manifest believing they have a backup depends on it.
What it carries
Plan and apply
Like terraform plan and terraform apply, and the cut is not cosmetic:
The plan does not ask for identity; applying does. The line is “mutating the database = signing”. Looking only reads, same as validating, and asking it for identity would lead people to skip it — precisely the step that avoids applying blind. Applying requires Administer through the same validator the panel uses: the CLI does not get a free pass for running on the server.
A single conflict stops everything. Applying “whatever can be applied” would leave the installation halfway between two descriptions, and nobody would know which one it is in.
It is idempotent. Running it twice duplicates nothing: the second plan says “no changes” across the board. Against the real installation, exporting and planning again gives 12 accounts and 5 flows with no changes — which is the proof that the file describes what is there.
Restoring: the other half
cauce importar creates accounts and flows. The credentials are poured in
by cauce restaurar, and they are two commands because they are two different
permissions over two different things.
In that order. restaurar does not create accounts: if the account does not
exist yet, it says so and writes nothing. The other way round there is nowhere
to write.
If the key is not the same, it refuses before touching anything. The
[cifrado] block carries the fingerprint of the key it was encrypted with; if
it does not match this installation’s, its blobs cannot be opened here. Writing
them anyway would leave accounts that look like they have a credential —the
health would say so, the panel would show them loaded— and the failure would
only appear at publish time.
And every blob is opened before being written: even if the fingerprint matches, one of them may come in truncated.
They end up degradada, not ok. The credential is loaded and nobody has
exercised it against the network. Declaring ok would assert that it works
without having checked — the false green exactly where it costs most, because
whoever restores walks away reassured.
What the import does NOT do
An import is additive and never deletes what the file does not mention. And there are three things declared in the file that are still not applied:
| The file says | The import does | Why |
|---|---|---|
consentimiento_vigente = "..." | Nothing. It warns | A permission is a personal, dated assertion; doing it in bulk would degrade it to paperwork |
activa = true | Creates it paused | Two pollers on the same Telegram bot steal each other’s updates |
auto_publica = true | Leaves it off | Something going out with nobody looking is decided by looking |
| an account that already exists | Does not rename it nor wipe its config | The handle is what people read in the panel; and a null would erase a tap somebody set by hand |
All three warn, they do not stay quiet. A silent import would leave somebody believing they had restored the installation and that it was already emitting — and they would find out otherwise several days later, when they noticed nothing had gone out.
Validation, before writing anything
cauce validar runs in full over the file, without touching the database.
It works just as well on a machine with no access to the installation: for
instance, in the review of a merge request.
An unknown filter key is an ERROR, not a field that gets ignored. Writing
tags instead of tag used to give a valid file whose flow published
everything: the engine ignored what it did not understand, and a condition
with no criteria filters nothing. The most dangerous filter is the one that
looks like it is there and is not.
Two ways of writing a criterion
The engine compares a single value the same way it compares a list, and the conditions the panel saves use the short form. Both are valid:
condicion = { tipo = "VIDEO" }condicion = { tipo = ["VIDEO", "CAROUSEL_ALBUM"] }On export it always comes out as a list: one form in the file, two ways of writing it.
This was missing, and the result was that cauce could not export the real installation: the export refused —correctly, rather than promising less filtering than there is— and left the file useless in exactly the installation it was meant to back up.
The criteria
They are met at the same time. “Videos by so-and-so about Palestine” wants all three things; with an OR, the flow would fire on far more than its author asked for.
| Criterion | What it looks at | Fail-closed |
|---|---|---|
tipo | Whatever the source declares | An item with no type does not pass |
tag | Topics, without #, normalised | An item with no tags does not pass: we do not know what it is about |
autor | When the source distinguishes author from account | An item with no author does not pass |
desde_dias | Time window | An item with no date does not pass |
desde_dias bounds what is EMITTED, not what is archived. The full history
still enters the archive; what this window prevents is a two-year backfill
turning into two years of publications.
When a key already belongs to another
Re-pointing the flow that already exists would send whatever is published afterwards to a different destination, without anyone deciding so. That is why it is an error and not a silent adjustment: the key has to be changed in the file.
A flow’s identity
Every flow has a clave: a stable slug, unique across the whole
installation and immutable, held up in the database by UNIQUE, NOT NULL, a format CHECK and a trigger.
It is not nombre. The name is edited and deleted: a renameable identity
would make every rename come in as a new flow on import, silently. A key that
already exists pointing at a different pair of accounts is a conflict that
gets reported, never a silent re-pointing.