Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

archive_keeper

Keeps the batches of timeranger2 archives that archive_senders (yunos/c/archive_sender/) ship with the native transport. It runs on the backup machine. Each channel is one sender, and each sender has at most one batch in flight. The frames are described in the README of archive_sender.

Where the batches go

<backup_directory>/<node>/<database>/<topic>/<key>/<batch>/
<backup_directory>/<node>/<database>/<topic>/<key>/.part-<batch>/     one in flight

What it refuses

Everything in a frame comes from the peer. These are refused with a WARNING, the answer refused and the removal of the batch aside:

A batch is refused once: the frames of it that the sender had already sent (its window) are answered refused too, but not logged nor counted again.

A frame whose header is not a json object drops the channel.

Configuration

AttributeDefaultMeaning
backup_directory—Absolute. Created if it is not there
allowed_nodes[]The node names whose batches are taken; ["*"] takes any node. Required: empty, the yuno does not start (ERROR)
input_service__input_side__The C_IOGATE whose channels are the senders
max_files100000The most files a batch may declare. The first archive of an hourly topic holds two files per hour (.json and .md2): 100000 is more than five years. Each file in flight costs the keeper some hundreds of bytes of memory
stale_session_timeout60000Milliseconds without a frame after which a channel is dropped at the next begin of any channel: its batch in flight is taken over (if that begin is of it) and the space it reserved is free again. 0: never
require_client_certificatetrueThe node of a batch must be the cn or a DNS name of the verified client certificate of its channel (mutual TLS). false: the node is the claim of the sender, said on purpose
{
    "global": {
        "archive_keeper.backup_directory": "/backups/yuneta",
        "archive_keeper.allowed_nodes": ["central", "edge"],
        "__input_side__.__json_config_variables__": {
            "__input_url__": "tcps://0.0.0.0:1999",
            "__ssl_certificate__": "/yuneta/store/certs/backup.example.com.crt",
            "__ssl_certificate_key__": "/yuneta/store/certs/private/backup.example.com.key",
            "__nodes_ca__": "/yuneta/store/certs/nodes_ca.pem"
        }
    },
    "services": [
        {
            "name": "__input_side__",
            "gclass": "C_IOGATE",
            "autostart": false,
            "autoplay": false,
            "children": [
                {
                    "name": "input_server",
                    "gclass": "C_TCP_S",
                    "kw": {
                        "url": "(^^__input_url__^^)",
                        "crypto": {
                            "library": "(^^__tls_library__^^)",
                            "ssl_certificate": "(^^__ssl_certificate__^^)",
                            "ssl_certificate_key": "(^^__ssl_certificate_key__^^)",
                            "ssl_trusted_certificate": "(^^__nodes_ca__^^)",
                            "ssl_verify_mode": "required"
                        },
                        "child_tree_filter": {
                            "kw": {
                                "__gclass_name__": "C_CHANNEL",
                                "__disabled__": false,
                                "connected": false
                            }
                        }
                    }
                }
            ],
            "[^^children^^]": {
                "__range__": 20,
                "__vars__": {},
                "__content__": {
                    "name": "input-(^^__range__^^)",
                    "gclass": "C_CHANNEL",
                    "children": [
                        {
                            "name": "input-(^^__range__^^)",
                            "gclass": "C_PROT_TCP4H",
                            "children": [
                                {
                                    "name": "input-(^^__range__^^)",
                                    "gclass": "C_TCP"
                                }
                            ]
                        }
                    ]
                }
            }
        }
    ]
}

__range__ is the number of senders that can be connected at one time.

Security. The node of a batch is bound to the client certificate of its channel (mutual TLS):

The CA of the nodes is a private one: a public CA does not certify a node name, and Let’s Encrypt no longer issues certificates for client authentication. Each node gets a certificate with its node_name as cn, and extendedKeyUsage=clientAuth. For example, with the openssl command:

# Once, on the backup machine: the CA of the nodes
openssl req -x509 -newkey rsa:3072 -nodes -sha256 -days 3650 \
    -keyout nodes_ca.key -out nodes_ca.pem -subj '/CN=Yuneta nodes CA' \
    -addext 'basicConstraints=critical,CA:TRUE' \
    -addext 'keyUsage=critical,keyCertSign,cRLSign'

# For each node (here "central"): its key, and its certificate
openssl req -newkey rsa:3072 -nodes -sha256 -keyout central.key -out central.csr -subj '/CN=central'
printf 'basicConstraints=CA:FALSE\nextendedKeyUsage=clientAuth\n' > client.ext
openssl x509 -req -in central.csr -CA nodes_ca.pem -CAkey nodes_ca.key -CAcreateserial \
    -days 825 -sha256 -extfile client.ext -out central.crt

Put the keeper’s port where only the nodes can reach it all the same.

Commands and stats

CommandDoes
list-sessionsThe batches being received, one per channel, with what each file has

Stats: batches_stored, bytes_stored, batches_already (offered and already here), batches_refused (batches, not frames: once per batch refused).

Traces: batches and frames, as in archive_sender.

Tests

tests/c/archive_shipping/: test1 runs the two yunos end to end (see the README of archive_sender); test2 runs the keeper against a fake sender that writes the frames by hand -- a forged sha256, too few of them, not lowercase hex, data out of the order of the files (and a frame of it already on its way: not refused twice), an empty batch, a batch of a kept name with other bytes: each refused, and nothing of it left in the backup. Over two links: a batch in flight on one is refused to the other and stored with the bytes of the first, and a batch whose link went silent is taken over by the other link.