Commit Graph
81 Commits
Author SHA1 Message Date
jschoubben fe7d4504bf mesh-vault: retire and rotate without the controller's lease
The controller ended by its unassignment and its lease key expired, so the
gate on a handed lease kept retirement, rotation and node-login rotation off
for good (issue 1126, A3). Drop the gate, the lease watch and the manifest's
read of mesh-controller.lease; vault_status no longer reports a lease.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:35:59 +01:00
jschoubben c84dfb5e0b Pin mesh-sdk at dff0ce2, the enrolment group's rebased head, so every member builds the same contract (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@db104bdb0838
mesh/delivery-group group feat/l3-enrol merged: every member is merged
mesh/check failed (2 tests): integration: mesh-mail-reader/cmd/mesh-mail-reader.TestAcceptanceOneMatteringMailSendsExactlyOneTellNamingIt: timed out w…
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:05 +01:00
jschoubben 67e4d4b062 Name the vault's enrolment wire test by its acceptance criteria alone, not by a review round (issue 774)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:42:29 +01:00
jschoubben f41a5a8df1 Vault: keep a rotation's hand acts in mesh-sdk's NodeLogin.Acts, and test a tunnel without its key or range
mesh-sdk now carries the hand acts that warranted a node's rotations as NodeLogin.Acts (ADR 0583 §2), so the vault's
own `acts` field beside the codec is gone: a used act is refused from the key's Acts, and a key written without them
keeps the ones recorded. The seat's tunnel takes all five fields; the wire table refuses an issue missing the machine's
tunnel key or the range as well. Built against mesh-sdk 4224665; mesh-vault stays at 134 unit tests.

(issue 774, 774/S43, 774/S46, review round 1b)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:42:19 +01:00
jschoubben 09824b4839 Vault: answer the enrolment verbs in mesh-sdk's wire shapes, certify the serving key presented, record each rotation's hand act
Review round 1 found that redeem could never succeed: the vault's answers were not the shapes mesh-enrolment decodes.
- enrolment-token reads `sealed` (not `show`), so asking for the sealed token no longer mints a new one; it answers
  delivery.EnrolmentTokenAnswer: at issue the bus address, its certificate fingerprint and the vault's signing key.
- Issue takes the machine's first tunnel (`tunnel`), keeps it beside the token and refuses a token whose Missing() is
  not empty, so the sealed token is one a machine can use (ADR 0587 §3, insight 2026-11-02).
- node-account answers delivery.NodeAccountAnswer: the login, and the password sealed anonymously to the presented
  raw X25519 key in base64, as the node-engine's SealingKey.Open reads it.
- A joining node's certificate is signed for the serving key presented at the redeem, only when the four presented keys
  have the digest node-account recorded; the name comes from placement (ADR 0587 §6).
- rotate-node refuses a hand act that is not rotate-node of this node, keeps the act id in the node's node-logins key
  (`acts`, beside mesh-sdk's fields) and refuses an id used before (ADR 0583 §2).

The enrolment test now calls the real tool handlers and decodes every answer into mesh-sdk's types, failing on an
empty field; it replaces the earlier enrolment test, so mesh-vault stays at 134 unit tests.

(issue 774, 774/S43, 774/S44, 774/S45, 774/S46, review round 1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:42:19 +01:00
jschoubben 272eff8b39 Vault: take the node-logins key and the enrolment token from mesh-sdk's contract
The vault wrote its own copies of the NodeLogin key and the token; the node-engine and the grants part read them through
mesh-sdk's codec (delivery.EncodeNodeLogin refuses a key without its version, a login that is not the node's, a stage
without a rotation). It now writes and reads through that codec and builds the token with delivery.Token, so the shapes
are one (ADR 0583 §1 insight, ADR 0587 §3). Built against the group's mesh-sdk with a go.work outside the worktree; go.mod
still pins the earlier mesh-sdk, for the lead to re-pin.

(issue 774, 774/S43, 774/S46, 774/S49)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:42:19 +01:00
jschoubben 049bd176c3 Vault: enrolment token and node account, signing for enrolment, a node's two logins and their rotation, own hand-out of a login
The vault serves the verbs enrolment-token, node-account, rotate-node, node-account-confirm and abandon, keeps the state
node-logins, and says rotation-begun, node-account-ready and rotation-done (ADR 0587 §3, §5, §8; ADR 0583 §1-§7 and
insights; ADR 0561 §4, §5). Only mesh-enrolment asks the first two and sign for an enrolment; a rotation needs a hand act
read back from mesh-hand-acts by its id, runs one at a time with the bus node last, and is ready only when the loaded list
holds the new login and done when it lacks the old one. A node's login is handed to that node's engine alone, sealed to
the key it stated. The key of a node that leaves the placement is deleted. Accounts kept for an enrolment or a node are
marked and never retired as unwanted.

Subtasks 774/S43 to S50 in one commit: they share enrolment.go, noderotation.go, tools.go and the manifest, and could not
be cut apart without a commit that does not build. New tests: 3 unit functions (table-style flows) and the one
integration test of S50; mesh-vault stays at 134 unit and 2 integration tests, within the module budget of 500 and 50, so
none was merged or deleted. The integration test's player gained a mesh/ask answer.

(issue 774, 774/S43, 774/S44, 774/S45, 774/S46, 774/S47, 774/S48, 774/S49, 774/S50)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:42:19 +01:00
jschoubben b3fcf408f2 Make tests that wait for concurrent work wait on the event or a condition with a long deadline (issue 1814)
A test that slept a fixed time and then asserted what a goroutine, process or
watcher did failed under the checker's load. Each now waits for its positive
evidence (a started baseline, a call counted, a state reached) within 30 s, and
the concurrency-limit tests meet at a barrier instead of overlapping by sleep.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 17:24:19 +01:00
jschoubben 3e08301b27 Pin every module to the group's mesh-sdk head after the gofmt fix
mesh/delivery-group group feat/judge-once rejected: composed together: the checker's verdict at the members' heads (5c42ba1fb5f3): integration: artifactstoreto…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: artifactstoretools/cmd/artifa… | 3e08301b27ea on 4b16c193f0fb | judged by the candidate's mesh-build-go at 8dd51540f73a
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 03:30:59 +01:00
jschoubben c4406da4a3 Pin every module to the group's mesh-sdk head after review round 1
mesh/delivery-group group feat/judge-once checking: 6 of 6 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking c4406da4a3fe on 4b16c193f0fb | novox/mesh-catalog#585 in group feat/judge-once
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 03:17:47 +01:00
jschoubben c19edac804 Pin every module that names mesh-sdk at the group's final head, without the judge (ADR 0578 §4)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 02:50:43 +01:00
jschoubben a40b4d3d3f Let the vault mint every wanted bus account whoever holds the controller's lease, keep its mint over a controller's issued value, and tell a node which modules it judged handed, so no account waits on a node reporting to the controller (issue 774) 2026-10-31 09:49:15 +01:00
jschoubben d959d37f8e Pin mesh-sdk at the group's head in the placement readers (issue 774) 2026-10-31 09:30:33 +01:00
jschoubben 85c336f260 Read the seat mesh-placement's state through the sdk's PlacementKeys in mesh-conditions, mesh-config, mesh-provisioner, mesh-vault and mesh-self-check, so they read its keys per module and node and the form before alike while it is delivered (issue 774, research 072 row 37) 2026-10-31 09:27:15 +01:00
jschoubben b60138990b Read a machine's placement once per answer of own, and say how long every answer took and every refusal, so an answer of 143 credentials is not 143 bus reads and an ask that went unanswered can be told from one never heard (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@dcf3950f7b07
mesh/check being carried to b60138990b34 on dcf3950f7b07 | from 0e78909a75b9
Quick fix: the plan reader keeps the machine's one placement key for the answer, as it keeps each module's assignment (issue 1126, 2026-10-28); the second time one answer's reads per credential held own past the node-engine's 15 s.
Thorough fix (not here): the node-engine asks own only when a secret or a credential it composes from changed, and the assigner writes no want that is unchanged.
2026-10-31 01:05:01 +01:00
jschoubben 6bd3da7736 Read every module whole for the plan-hands check, so its verdict follows their source, and judge a shared value's consumer by whether its plan hands it; leave mesh-provisioner out of the group (review round 1, issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@d412991c6992
mesh/delivery-group group fix/plan-hands merged: every member is merged
mesh/check being carried to 6bd3da773683 on ca48943b925a | from mesh-sdk 125bb9177189 | novox/mesh-catalog#574 in group fix/plan-hands
2026-10-31 00:00:46 +01:00
jschoubben 1b0c09763e Pin mesh-sdk 17fc9b9 in mesh-vault and mesh-provisioner-client for Plan.Hands, and say what providerPlan answers now that a plan of provisions alone exists (issue 774) 2026-10-31 00:00:46 +01:00
jschoubben 3c3d018dbc Ask mesh-sdk's Plan.Hands wherever the vault and the provisioner client judge a module handed, and refuse in the catalogue check any line that takes a plan existing for it, so a plan of provisions alone never again withholds a module's secrets (issue 774) 2026-10-31 00:00:46 +01:00
jschoubben d5c4f8aaa9 Answer a module's own secrets to its node-engine when its plan only keys a provision, so twenty modules whose route the provisioner now keys compose again (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@ca48943b925a
mesh/check being carried to d5c4f8aaa968 on ca48943b925a | from 5be46a80cb35
2026-10-30 23:48:29 +01:00
jschoubben 2455445dab Pin mesh-sdk a3718126, whose bus retry never asks again a call its callee answered (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@7e905a66944e
mesh/delivery-group group fix/sweep-2026-10-30 merged: every member is merged
mesh/check checking 2455445dabee on 72cee59acc32 | novox/mesh-catalog#565 in group fix/sweep-2026-10-30
2026-10-30 18:43:13 +01:00
jschoubben bc433b8304 Take the compiled mesh-vault program out of the tree and ignore it, since build output never belongs in the catalogue (issue 774) 2026-10-30 18:40:50 +01:00
jschoubben 647c5c3c06 Pin nine modules to the fix group's mesh-sdk, 206f794e, which carries Follow, the bus retry and the forms list (issue 774)
mesh-provisioner, mesh-config, mesh-issues and mesh-healers need KeptState.Follow; mesh-build-go, -image and -node the
retry of a state write the bus delivered to no one; claude-code asks.Condition; mesh-vault the credential forms list.
Each passes go vet, go vet -tags integration and go test on this pin, built with no go.work.
2026-10-30 18:39:49 +01:00
jschoubben 434214e615 Judge a bus account by its declaration, never by a broker- prefix, so the controller's address is kept (issue 774)
683fd547 and b7f87822 took every `broker-<id>` own secret for a bus account. mesh-controller's broker-address is the
bus's address (MESH_BROKER_ADDRESS_FILE, read by broker.FromEnvironment to issue enrolment tokens and credentials):
c1 would have refused to keep it, and it was counted among the "owed bus-account hashes" every minute.

A name is now a bus account when it is declared: `broker` or `provisioner-broker`, or the own secret of an account the
grants part wants, which it composes from the manifests' bus processes (what catalogue.ToolsBusSecret names). A
`broker-<id>` holding an account the controller issued before its want was heard is still hashed. Anything else is
kept as given, never refused, never minted over, never hashed and never owed. The mint path replaced only wanted
names already, so the address was never at risk of replacement there; the test now proves it.

Quick fix: except broker-address by name. Thorough fix: the declaration decides, for every name. Chose thorough.
Table rows: broker-address kept as given, not replaced, owed nothing; broker-nats-tools replaced by a minted account
once wanted and refused a plain value after. They fail on the previous code.
2026-10-30 18:38:59 +01:00
jschoubben f3b13de20d Answer a move the vault refuses for holding another value with fields, not words alone (issue 774)
move-in's refusal "the vault already holds another value for ..." was an error's text, which the node-engine and the
controller could only quote. It is now an answer: `refused` with the reason holds-another-value and the held value's
record (generation, changed by, changed at, origin; never the value), `why` in words, and no read-back, so an
engine that does not read `refused` still fails as before ("answered with no read-back"). keepLocked's refusal is a
typed error (HeldAnother) whose text is unchanged for every other path.

Quick fix: a stable prefix in the text. Thorough fix: the fields. Chose thorough, as asked; mesh-host's
moveIntoTheVault has to read `refused` to use them.
2026-10-30 18:30:45 +01:00
jschoubben b7f878226e Replace a held bus-account value that is no account by a minted account when its user is wanted (issue 774)
The three random values kept before the vault judged them (own/novox/nats/broker-nats-tools,
own/novox/mesh-controller/broker-address and broker-management) are held, so minting refused them ("held already")
and their users stay off the bus. A value that is no account has no hash and logs in nowhere, so replacing it
breaks no login the grants part composed. When the grants part wants the user (every want is heard at the vault's
start), the vault now mints an account in its place, judged again under the store's lock, keeping the value it
replaces beside it. A held account is still never minted anew (ADR 0561 §4).

Quick fix: let an operator rotate the three by hand. Thorough fix: the mint path replaces any such value, so no
other one stays behind. Chose thorough.
2026-10-30 18:29:48 +01:00
jschoubben 683fd547b2 Keep a bus account in the vault only as an account: a random value for a broker name is not issued (issue 774)
own/novox/nats/broker-nats-tools (moved in today) and mesh-controller's broker-address and broker-management (moved in
from the controller's store) are random values, not the account JSON a bus user is hashed from, so the hash step says
"not JSON" and the grants part leaves those users off the bus. The controller made them (fixed there, c421629f); the
vault kept them, since nothing it keeps judged a covered name's value.

Quick fix: refuse a plain value in move-in. Thorough fix: keepLocked, which every way into the store passes, refuses
for any covered name (broker, provisioner-broker, broker-<binary>, which catalogue.ToolsBusSecret names) a value that
is no account, saying "not issued". Chose thorough: one rule where every value is kept.
2026-10-30 18:29:01 +01:00
jschoubben 2066021fca Read the vault's credential forms from mesh-sdk's one list, as the provisioner client does (issue 774)
pair.go kept its own copy of the forms a plan may name; the copy in the provisioner client went in #564, after it
refused plans naming `none` while the vault's copy took them. Quick fix and thorough fix are the same here: the vault's
constants are mesh-sdk's (catalogue.CredentialForm*), and a form is judged by catalogue.IsCredentialForm, so a new
form is added in one place. No new test: behaviour is unchanged, and the existing form tests pass on the shared list.
mesh-vault's mesh-sdk pin moves to 9379e9a9, the provisioner client's, the first with the list.
2026-10-30 18:22:09 +01:00
jschoubben 35ff67bda0 Design 47's last milestones: the keeper, self-check, healers, hand-acts, provisioner, vault, artifact sweep, disk readings, credential forms (issues 774, 1952, 1953, 1954, 1955)
mesh/delivery-group group feat/774-l3-reland rejected: composed together: the checker's verdict at the members' heads (b48c90fea31f): integration: mesh-self-ch…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: mesh-self-check/cmd/mesh-self-check.TestTheSelfCheckWholeRunsOnTheBus: the run answered {Run:doctor-1793351737-1 At:20…
One commit with exactly the tree of the reviewed head 35b4509c0a, on main 12c2574c9d, so two compiled programs added and removed on the branch leave no trace in history (issue 1577).
2026-10-30 02:21:51 +01:00
jschoubben c8240c38bb Revert L3 (mesh-catalog, merge 15552413): its node-engine removed every held module on novox, the bus server among them, at 08:54 CET on 2026-10-29 (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@030f9ee4e780
mesh/delivery-group group revert/774-l3 merged: every member is merged
mesh/check being carried to c8240c38bb89 on 030f9ee4e780 | from 1555241393fe | novox/mesh-catalog#557 in group revert/774-l3
L3 lands again with two fixes: a held module is never removed, and the grants cover L3's new subjects.
2026-10-29 09:42:30 +01:00
jschoubben c193d2b389 Pin mesh-sdk 8eafc0c, the L3 group's head on mesh-sdk main with #120 and #121 (issue 774)
mesh/delivery-group group feat/774-vault-mints-l2 ready: every member ready, and composed together they pass
mesh/delivery closed: a newer head of the same pull request
mesh/check pass c193d2b38970 on 158348ede789 | unit 1319 run, 5401 kept, 47 skipped | integration 1359 run, 5490 kept, 49 skipped | over bound: unit, …
2026-10-28 21:56:06 +01:00
jschoubben 5be6576117 Follow L2 under L3: the vault and mesh-config read the seat mesh-placement's placement, and the check that only listed modules read the controller's assignments moves into noplacement, as L2 deleted the placement check (issue 774, ADR 0567, ADR 0549 §3, §6) 2026-10-28 21:54:36 +01:00
jschoubben 24fad2b92a mesh-vault: pin mesh-sdk 4e6e5cc, the L3 group's head rebased on L2 (issue 774) 2026-10-28 21:54:36 +01:00
jschoubben d3eb5cae80 mesh-vault: secret-ask answers the callers its manifest names, each named as the runtime proved it, within the same bounds; the controller's relay as before (issue 774, ADR 0563 §8) 2026-10-28 21:54:36 +01:00
jschoubben c00005c141 mesh-vault signs: its signing key and authority made once with their operator copies, the verb sign for a node's own certificate from bus state, the event signed, the state signers and the tools that read and reassert them (issue 774, ADR 0550 §2) 2026-10-28 21:54:36 +01:00
jschoubben e6b913cac7 mesh-vault: account_retire refuses while the controller holds the lease or before the wants are read whole, as the passes do (issue 774, review round 1) 2026-10-28 21:54:36 +01:00
jschoubben 411c1b8de8 mesh-vault: prove the vault whole on a real bus mints only from the lease write, and try a failed mint again at the pass; adopt the operator key before the watches open (issue 774, ADR 0561 §1-§4) 2026-10-28 21:54:36 +01:00
jschoubben 64e6688ec8 mesh-vault: tell where modules stand and which node holds the bus from the catalogue's placement first, so the vault does not depend on the controller's assignments for them (issue 774, ADR 0549 §3, §5) 2026-10-28 21:54:36 +01:00
jschoubben 2d57c9663a mesh-vault: serve issue-account, the hand path that mints one wanted account now, and record a rotation at most once per loaded list's revision; a covered account still never rotates in place (issue 774, ADR 0561 §4, §9) 2026-10-28 21:54:36 +01:00
jschoubben 5c1729523b mesh-vault: retire an account the grants part no longer wants only within ADR 0230's bounds, keeping its value, and re-enable it when wanted again; account_retire for a person past the bound (issue 774, ADR 0561 §2) 2026-10-28 21:54:36 +01:00
jschoubben 1628db6c80 mesh-vault: mint only while the controller's lease is handed away, read afresh before each mint, and say in vault_status what is held back and why (issue 774, ADR 0561 §3, §9) 2026-10-28 21:54:36 +01:00
jschoubben 0b9f19b612 mesh-vault: mint each account the grants part wants and the vault holds no value or adopted hash for, in the controller's shape, hashed before account-issued and secret-changed (issue 774, ADR 0561 §1, §2, §4) 2026-10-28 21:54:36 +01:00
jschoubben 59cad7154a Pin mesh-sdk to the group's head 38dbe1d in the build modules, mesh-config, the gate and the vault; the vault's test answers by proposal (issue 1126) 2026-10-28 18:52:35 +01:00
jschoubben a9d96b30be mesh-vault: a watch of the assignments begun again keeps nothing an earlier attempt took, and a module the mirror lacks is read directly before a credential is refused (issue 1126, review round 1)
mesh/delivery merged: source.main moved to novox/mesh-catalog@85bbeb37e3e0
mesh/delivery-group group fix/1126-vault-own merged: every member is merged
mesh/check pass a9d96b30be8d on 69056948bfa8 | unit 1639 run, 4201 kept, 46 skipped | integration 1674 run, 4215 kept, 47 skipped | over bound: unit, …
A watch that failed partway kept what it had taken; begun again, the runtime does not hand a key deleted meanwhile, so
a removed assignment read as standing and credentialRefusal could hand a consumer credential to a node no longer
running its module. Each attempt now begins from nothing held, and a late delivery to an older attempt changes nothing.
A 'not on the node' refusal is confirmed by a direct read first, since a watched mirror can be behind.
2026-10-28 14:55:36 +01:00
jschoubben 277a4d0060 mesh-vault answers own in one pass: assignments from its watch, each module's once, one synced record per answer (issue 1126)
Seed-9's probe (13:31-13:36Z): novox's own of 57 secrets and 146 credentials took 26 s, past the node-engine's 15 s,
and ace and novox refused their whole shadow answer. own read the controller's assignment over the bus once per secret
and per credential (203 reads), read every record of every node, and synced the hand-out record once per value, under
the one lock every node's answers share.

Now the vault watches the assignments mirror (a watch, never a poll) and reads it from memory once caught up, over the
bus until then; one answer reads each module's assignment once; only own/<node>/ records are read; and every hand-out
of one answer is recorded in one synced write, none handed when it cannot be.

Quick fix weighed: a longer bound in the node-engine (VaultWithin). Thorough fix chosen: the cost grows with every
module and every node, and the lock is shared by every shadow answer and every apply.
2026-10-28 14:47:20 +01:00
jschoubben 0974d1a57d mesh-vault's own_drift row carries the id as a node runs it, its module's name first, and the vault pins mesh-sdk 3028c7e (issue 1126, cause C4; review round 2)
mesh/delivery merged: source.main moved to novox/mesh-catalog@9fd95693db54
mesh/delivery-group group fix/1126-seed-same merged: every member is merged
mesh/check being carried to 0974d1a57d99 on 2a03c106f4b9 | from 9fd95693db54
2026-10-28 10:02:46 +01:00
jschoubben a17356a162 Fill go.sum for mesh-sdk 1062b33, the group's head (issue 1126) 2026-10-28 09:53:34 +01:00
jschoubben 35ad58e6e2 mesh-vault pins the group's mesh-sdk head 1062b33 (issue 1126; go.sum is filled once that head is pushed) 2026-10-28 09:51:48 +01:00
jschoubben da5da4f6f7 The vault takes what runs through move-in's replace_running, not a verb of its own, so no seat promises anything new (issue 1126, cause C4) 2026-10-28 09:48:14 +01:00
jschoubben 671a6a1f47 own-take-running has a person's switch, own_take_switch, kept in the vault's store (issue 1126, cause C4) 2026-10-28 09:39:16 +01:00
jschoubben 47ed384d97 The vault takes the value that runs on a machine over the one it was first handed, from that machine alone (issue 1126, cause C4)
move-in never replaces a value the vault holds, so where the controller made an own secret anew after the move, the
new value runs while the vault keeps the first. The verb own-take-running takes it from the node-engine of the machine
it is for, keeps the replaced value as the previous generation, raises the generation and says own-replaced without a
value; own_drift lists every own secret a node's shadow answer says another value of, mesh-wide. The module needs the
mesh-sdk head of fix/1126-seed-same-own-resync (delivery.OwnValuesDiffer, the seat's verb).
2026-10-28 09:37:54 +01:00