Commit Graph
52 Commits
Author SHA1 Message Date
fenleon 2b96445cb4 registration: the NAC mint is exonerated on live data (rev 47)
NacValidationTest.mintReproducesLiveVendoredStamp reproduces the accepted
genuine prefpane stamp byte-for-byte from recovered live inputs: the rngPad130
is recoverable from the stamp itself (domergestep2 is XOR-invertible —
dm(x, in2) = state(in2) XOR x), session250 is the genuine descrambler's own
inversion, and the scramble pad is thread-RNG filled inside scramble_body
(second code path, disasm 0x179654d) — the session-32 'rng_pad130' verdict is
corrected. Live A/Bs eliminate the TLS transport (ClientHello byte-equivalence),
request shape, and staleness; the prefpane ICLOUD_INVALID_DEVICE_TOKEN blocker
narrows to the machine's IDS registration freshness (the reference runs
wa/register x2 before the delegate sign-in; a pre-prefpane register on our side
answers 6004 transient-retry). Spec rev 47 + NAC-NOTES session-33 addendum.
2026-10-16 17:44:45 +02:00
FENN 70aef73b19 Update README.md 2026-10-16 08:35:27 +02:00
FENN 3d121a0368 Update README.md 2026-10-16 08:32:46 +02:00
fenleon 490e92e9cf README: the full registration chain completed live (2026-10-16); the self-minted stamp is the last gap 2026-10-16 08:26:12 +02:00
fenleon 3f11eab49f registration: postdata presents the login run's anisette set (one set session-wide)
The A/B ladder 2026-10-15/16: fresh postdata mint was not the prefpane discriminator —
the prefpane now passes with a fresh GENUINE stamp through the whole chain, pinning the
400 on the validation-data mint itself (rng_pad130 of scrambleBody).
2026-10-16 00:07:32 +02:00
fenleon 2ff994751c registration: the prefpane presents the LOGIN RUN's anisette set (session-wide reuse, wire-pinned)
The trapped-binary decode (2026-10-15) shows ONE anisette mint serves the whole session — GSA hops, 2FA validate, postdata, both prefpane POSTs — so the PET is paired with the OTP window it was issued under. The rev-25/26 'fresh set at the sign-in POST' reading is corrected (confounded with the replay-guard era). GsaLoginChain mints one cpd per login run and exposes it as lastLoginAnisette (Client-Time frozen at the mint); GsaDelegateLoginClient's prefpane branch reuses a supplied Client-Time instead of re-minting. VEN step-2 wire fact recorded in spec rev 46: zero anisette on initializeValidation, no step-2 binding. Also wire-proven live: the GSA init 'ps' field is REQUIRED (omission answers ec=-80001).
2026-10-15 18:57:00 +02:00
fenleon 9da53e31f8 registration: GSA cpd/headers wire-pinned against the trapped binary (2026-10-14)
The first-ever capture of the binary's GSA init/complete/postdata/2FA requests
(static OpenSSL via ssl3_write_bytes; rustls only ever saw the prefpane path):
- cpd carries NO X-Apple-I-MD-LU (neither headers nor cpd) — dropped; the
  keychainIdentifier constructor param is gone with the derivation.
- cpd 'at' is the plist INTEGER 0 (the boolean-false recording was also
  live-accepted; the integer form is what the binary sends).
- GSA login headers: Accept */* added; casing aligned to the binary
  (X-Mme-Client-Info, X-Apple-Ak-Context-Type).
- 2FA browser-style set: hardware-identity headers ride it as HTTP headers
  (unlike the login POSTs); same casing alignment.
- ptkn rides every login cpd once the app holds a push token (imessage side
  wires the persisted courier token).
2026-10-14 20:23:41 +02:00
fenleon f47fc49eaf registration: GSA login-shape parity per the vendored source (2026-10-14 diff)
- cpd gains the hardware-identity headers (SRL-NO/MLB/ROM chained last) — the
  vendored get_cpd_data carries them in the login BODY; the gsservice HTTP
  header set does NOT (the earlier reading had this inverted).
- GsaHeaderConfig.toHeaders(): drop the hardware headers, add Accept-Language
  (get_gsservice_headers carries it).
- ProvisionedAnisette flavor doc corrected (FLAVOR_MAC=1/FLAVOR_IOS=0 — the
  inverted doc caused the failed vendored-state swap, GSA ec=-22421).
- 'at' stays the recorded boolean false (vendored source writes Integer(0);
  both live-accepted).
2026-10-14 17:40:40 +02:00
fenleon 478a473629 registration: prefpane parity + the GSA postdata liveness event + the live sig16 vector
- GsaDelegateLogin: the prefpane anisette headers carry the accepted request's
  exact wire casing (X-Apple-I-Md / -Md-M / -Md-Rinfo / X-Apple-Adsid /
  X-Apple-I-Timezone — real AuthKit forms; the recorded GSA names are all-caps
  and setup.icloud.com answers ICLOUD_INVALID_DEVICE_TOKEN with them at full
  value parity). GSA keeps its recorded working set.
- GsaHeaderConfig: hardwareHeaders (X-Apple-I-SRL-NO / MLB / ROM) ride every
  GSA call — the reference chains the C58 machine's headers into all of them.
- GsaPostdataClient wired: the postdata liveness event fires before the
  delegate sign-in (vendored do_login order — the earlier 'sign-in first' note
  was wrong), with the APNs push token (the endpoint 401s ec=-800012 without
  it); this is what creates the trusted device record on the account.
- NacValidationTest: the live vector — sign() reproduces the vendored client's
  real sig16 byte-exactly on gdb-captured inputs (the mint's signature model
  exonerated on live data).
- ActivationDryRunTest: postdata ordering corrected to the vendored sequence.
2026-10-14 16:57:07 +02:00
fenleon e5352e77ce registration: step-2 optional extraHeaders (A/B instrumentation — anisette-on-mint falsified live 2026-10-13) 2026-10-13 16:35:03 +02:00
fenleon 7980dd1fe2 courier: stop() closes the socket to unblock the blocking reader (soTimeout=0) — stop used to hang until the keepalive watchdog (~180s, observed live), stalling everything after a one-shot connect 2026-10-13 14:40:24 +02:00
fenleon 6a991b0165 spec rev 45: prefpane/login = V1 AuthRequest protocol (RE'd from vendored rustpush); 400 ICLOUD_INVALID_DEVICE_TOKEN persists across body shapes → account/device-state gate 2026-10-13 13:23:49 +02:00
fenleon 3379a52d8c gsa: prefpane V1 body keeps Basic auth (live 2026-10-13: V1 body without Basic → 401 UNAUTHORIZED; the closure's basic_auth needs no string constants) 2026-10-13 13:11:20 +02:00
fenleon 863a2d82cb gsa: prefpane/login sends the vendored V1 AuthRequest body (apple-id/client-id/delegates/password, no Basic header) — RE'd from the vendored open_absinthe .so (V2 symbols absent); v2 endpoint keeps the recorded V2 shape 2026-10-13 13:00:37 +02:00
fenleon 2537a80629 gsa: delegateEndpoint seam + PREFPANE_ENDPOINT (the reference client's delegate sign-in endpoint); fresh-PET note 2026-10-13 12:22:07 +02:00
fenleon e6d8d45bef fairplay: out-of-repo identity override — the committed identity is a placeholder (live verdict SIGNATURE_VERIFICATION_FAILED); real pair accepted live; spec rev 44 2026-10-13 11:42:08 +02:00
fenleon e44d501ca5 albert: restore the accepted request shapes (ActivationInfoXML as plist data + rustpush UA); AppleTrust.platformTrustManager() for the public-CA endpoints; spec rev 43 2026-10-13 10:41:36 +02:00
fenleon c95b91d5d7 registration: derive the pear-AES IV from the blob slot (blobFieldMap^-1) — closes the 5080; spec rev 42 2026-10-13 10:09:11 +02:00
fenleon c62c882652 OTP wiring: two=client_secret (mid was fed — the real -29004 root cause); bare-dict plist fragments (GSA spd); step-2 error detail; spec rev 40-41 2026-10-13 09:30:49 +02:00
fenleon 59070c37f0 OTP: live-corrected inputs (two=client_secret, Mac flavor byte 1) + h-block fix; tail divergence recorded (rev 39)
Live GSA A/B + gdb capture of the working client (captures/c58live/):
- gen_otp(gen=unix_seconds/30, one=metadata(400B), two=client_secret(32B))
  — the rev-27 '32-byte mid' reading was another mid/secret swap
- Mac machines dispatch gen_otp (OTP flavor byte 1) — FLAVOR_MAC=1
- error ladder: -22421 = structurally invalid OTP; -29004 = wrong sig payload
- genOtp stage B now writes ctx[0x110..0x120] = LE [h3,h2,h1,h0] (the fitted
  'never read, [h3,h1,h2,h0]' reading was wrong)
- Kotlin gen_otp still diverges from the binary in the final mixing rounds
  (post-table ctx byte-identical; sig differs) — ground truth + ctx
  checkpoints captured for the round-state hunt
2026-10-13 07:25:38 +02:00
fenleon bcb016b2a8 JitPack publishing (maven-publish on all modules), docs/USAGE.md, CI + weekly drift probe v0.1.0 2026-10-12 23:36:32 +02:00
fenleon 2fac87ca42 README: cut to the point 2026-10-12 23:22:21 +02:00
fenleon 40ec9bab3e README: the 5003 EM gate is closed (rev 38), live provisioning runs 2026-10-12 22:51:44 +02:00
fenleon f6ee0ae323 AppleTrust: expose the embedded root pool for rustls-backed transports
The ids-bridge (imessage) takes its trust anchors per call from this pool —
the VEN endpoint's TLS fingerprint gate (rev 38) requires a rustls-shaped
handshake, and the JVM stacks cannot provide one. Pure visibility change.
2026-10-12 22:48:28 +02:00
fenleon 543178fc2a Spec rev 38: the step-2 5003/EM gate is a TLS-stack fingerprint (rustls accepted, OpenSSL rejected)
The user's 2FA re-login re-fired the app's own IDS registration; gdb traps
showed the full mint chain ACCEPTED live (key_establishment + sign after a
successful step-2 parse, x3). Fresh-genuine-blob transport matrix: reqwest
0.11 + rustls 0.21 (+ the vendored baked Apple root DER) gets a real
session-info response on the identical XML-plist envelope; curl-OpenSSL over
h2 and h1.1, and curl with UA/Accept stripped, gets 5003/EM on the same
blobs. HTTP version and headers are irrelevant — the VEN endpoint
fingerprints the TLS ClientHello.

Kotlin blob/framing fully vindicated; the tool's :server needs a
rustls-shaped handshake (implementation decision open).
2026-10-12 21:37:40 +02:00
fenleon c10551fc97 Live-provisioning wire fixes (rev 37): capitalized Header/Request keys, lookup needs angle-format X-Mme-Client-Info
Both found by running ClearAnisetteProvider against Apple's live endpoints
(first live Kotlin provisioning run — succeeds after the fixes):
- lowercase {header, request} on the start/finish POST gets a silent HTTP 200
  with an EMPTY body; {Header, Request} gets the real Response.{spim,ptm,tk}
- the GSA lookup GET 404s without X-Mme-Client-Info and only accepts the
  angle-bracket form <Model> <macOS;ver;build> <com.apple.AuthKit/1 (…)>

Also records the session-26 step-2 EM A/B: the 5003/EM gate reproduces on a
FRESH genuine blob (ValidationCtx::new under gdb, replay-state eliminated)
and with the full anisette header set — anisette-on-step-2 falsified; the
app's own-flow verdict is blocked on interactive IDS re-registration.
2026-10-12 18:36:29 +02:00
fenleon 5aba5f522d C56-c closed: HardwareConfiguration.toBin + ValidationCertChain (rev 36)
- toBin(): the closed HardwareConfig::to_bin grammar — write_str/write_item prefixes,
  model, MAC (raw 6 B), serial, UUIDs, board, build, the 00 03 0b 52 constant, then
  the seven binary items in C58 export field order (serialEnc, platformUuidEnc,
  rootDiskUuidEnc, ROM, romEnc, MLB ASCII, mlbEnc). Corrects the rev-32/35 label:
  the raw second field is the MAC, not the ROM (multicast-bit + stub-type evidence).
- Round-trip verified byte-exact against the live 250-B session250 capture.
- ValidationCertChain: the step-1 cert data's framing (01, count, per cert
  u16be type, u16be len, DER; leaf LAST = DRM Technologies A01, the OAEP key cert).
- NacMint/NacValidation: rngPad130 -> rngPad, generalized to 0x17c - session250.size.
- Live smoke result recorded: step 1 OK vs Apple, step 2 returns 5003/EM — blob
  construction re-verified against the replay dumps; residue is outside the blob
  (spec rev 36, NAC-NOTES session-26). 217 tests green.
2026-10-12 16:59:18 +02:00
fenleon f4be1f638f C56-a closed: NacRequestBlob (rev 35) — the 338-B session-info-request blob fully derived
Direct in-process replay of ValidationCtx::new (two byte-verified runs, no Apple
network): pearKey16 = the first RNG16 draw (= init_key input = OAEP msg[0:16]);
the post-encrypt 'xor' loops are byte-substitution tables (0x2249f9/0x224af9,
appended to PearTablesData); the pear-AES plaintext = substitute(02 ‖ u32be(1) ‖
innerKey16 ‖ zeros(10)); the encrypt_cbc iv rides an in/out 16-B block.
NacRequestBlob builds the blob with platform OAEP + HmacSHA1; 4 synthetic-vector
tests; :registration:test 213 green. Remaining live-mint gate: C56-c only.
2026-10-12 10:48:13 +02:00
fenleon ca057376ee C56-a request-blob field map ~85% pinned: OAEP(pearKey16+hmacKey20) + HMAC-SHA1 tail verified (rev 34) 2026-10-10 08:06:02 +02:00
fenleon 6eca991a7a Sign-model fresh-input divergence closed: P region is input-dependent (rev 33)
Two session-20-frozen constants falsified on fresh live inputs:
- staging i==10 ecx2 = perm2u[st[0x6fa2]] (was frozen perm2u[0x40])
- the P region (state+0x6898, 7x256): five of seven tables are parameterized
  on five body-derived byte-XOR pairs by the SSE builder at 0x17a1381-0x17a20b5
  (tables 2/5 constant). The builder's instruction stream is serialized into
  generated NacPTable.kt (1833 ints + 46 const vectors) and re-executed
  lane-wise, byte-exact vs the emulated builder on fresh + random + s20 states.

NacSign re-ported with every body-derived region computed: u32tab/nacMasksFlags
(12-step barrett chain, full 128-bit product), mapKey cursor0, dynamic N,
base-7 0x6fd8 selectors, region6f98(state) (tsel6f98 span, 4 tables strided
0xf0), masks/flags at 0x6fe8/0x6fec, scatterMutated & ~masks, cipher RC =
state 0x25d8 words. The four former state-embedded constants (region6f98/
sel6fd8/maskC68/pTable) dropped from NacSignTablesData.

Verified: fresh live sig16 307d7151... and the full fresh 517-B stamp
byte-exact vs the live capture; session-20 stamp still byte-exact;
sig_hand.py verify 20/20. 209 registration tests green (vectors regenerated).
Spec -> rev 33. Method + dumps: registration/dev/clearadi/captures/NAC-NOTES.md
(gitignored).
2026-10-09 08:42:30 +02:00
fenleon 075857e79e C56 input mappings pinned from three live runs (rev 32); sign-model fresh-input divergence recorded
keyEstablishmentFull (payload + trailing Established); NacMint facade
documents the wire-pinned mappings: pearKey16/rngPad130/rand16 = fresh
client RNG, session250 = HardwareConfig::to_bin blob, body576 =
established[2:578], cert79 = response trailing[0:79] (NOT the step-1
cert — falsified). C56-a field map + HardwareConfig grammar stay open.
NEW RESIDUE: the rev-30/31 sign model mis-signs fresh live inputs (all
four sign::sign args byte-verified) — live mint blocked on closing it.
209 registration tests green.
2026-10-08 21:19:13 +02:00
fenleon cfd6cd4e71 NAC sign ported to Kotlin (NacSign): full mint in-repo; spec rev 31; NacMint facade
The verified pure-python sign model (sig_hand/sig_init/sig_tail) is now
transcribed to Kotlin: NacSignTablesData.kt carries 102 tables as generated
base64 source (ClearAdiTablesData precedent, provenance header), NacSign.kt
runs the pipeline (u32tab → scatter → perms → keygen → phases B–E → rounds →
genBufs → threshold-cipher tail), and NacValidation.mint computes sig16
internally — the full 517-B stamp from Kotlin inputs. Pinned by 5 synthetic
LCG vectors against the python model (208 tests, 0 failed; table images
re-decoded and compared table-by-table, 32/32). Spec rev 31; README status
updated. NacMint: minimal public facade for consumers (the imessage tool
wires ClearAnisetteProvider + the mint behind its AnisetteSource/
ValidationDataSource seams).
2026-10-08 19:35:39 +02:00
fenleon ebabd37aef NAC sign closed: full validation stamp computes pure-code from inputs; README updated
The tail threshold cipher and pre-init region closed via empirical fit
against the 270 checkpointed rounds — pure-Python model (no emulator, no
captures) reproduces sig16 and the full 517-B stamp byte-exact (verify
20/20, independently re-verified). Spec rev 30 residue line corrected;
README: anisette chain now includes the NAC mint (Kotlin signature port
next).
2026-10-08 18:18:18 +02:00
fenleon 6e50bf6602 NAC mint (C5): pear_aes + blob/framing decoded byte-exact; Kotlin PearAes/NacMerge/NacValidation; spec rev 30
- pear_aes white-box (9-round CBC, custom whitening/final maps, establish_key
  T-transform) byte-exact vs 3 live runs; domergestep2 21/21; key_establishment
  EstablishKeyResponse wire pinned.
- Full 517-B validation stamp computes byte-exact from (cert, session, rng_pad,
  body, rand16); SignState::sign structurally decoded (stages 1-9 hand-verified,
  tail cipher checkpointed) — the capture-free tail transcription is the one
  open residue; mint(sig16) takes the signature as a parameter.
- Kotlin: generated PearTablesData/PearTables (base64 table image), PearAes,
  NacMerge, NacValidation; 206 tests green (synthetic vectors, no Apple bytes).
- Spec rev 30; method/findings in gitignored registration/dev/clearadi/captures/.
2026-10-08 07:14:50 +02:00
fenleon 77b9819de7 ClearADI cpim tail closed: X = SHA-256(encout || sig(trailing[0:16]) || const) — wire fully pinned
Two further live provisioning runs (gdb_prov8/9) close the last three
TODO(capture) items of the cpim:

- X (the 32-byte tail group) = SHA-256(encout || spim_map_signature(
  trailing[0:16]) || fixed 24-byte filler), written to the wire byte-exact;
  verified against both runs.
- The signature input is the spim trailing's first 16 bytes (server
  material, varies per session) — the earlier 'constant block' reading was
  a stale session's value; the signature model matches the live output
  exactly.
- The 12-byte plaintext tail is 12 fresh RNG bytes (same thread-RNG pool
  construction as the IV).
- trailing[0:16] and the sig/digest 'nowhere on the wire' mystery resolved:
  both ride transitively through X.

Kotlin: CpimShaTailSeal (the pinned production seal) wired as the provider
default; doc comments updated; one synthetic-vector test. Spec rev 29.
195 tests green.
2026-10-07 18:13:23 +02:00
fenleon 21eefe3846 ClearADI: live-capture corrections — ProvisionedData mapping, PTM layout, cpim outer message
One real OpenBubbles provisioning run under gdb (5 attempts, method + dumps in
registration/dev/clearadi/captures/WIRE-NOTES.md) pins what the previous
session left TODO(capture):

- ProvisionedData = {metadata Vec (400 B = decrypt out[4:404], len u32 BE at
  out[0]), client_secret = RNG draw 2, mid = RNG draw 3, flavor u8}; the finish
  gate is the decrypt output's out[408:440] == draw1 echo (not out[0:32])
- PTM = [u32 BE 4][seed 16][u32 BE payloadLen][payload][tail]; the decrypt
  chain seed rides in the PTM itself (finish 17f2557 rcx = ptm[4:20], live)
- cpim = a 276-byte OUTER message [u32be(5)][iv 16][u32be(160)][encout 160]
  [X 32][u32be(52)][u32be(4)][Y 52] over the 160-byte plaintext
  [len][encsec(rng2)][len][rng1][blob=mid][arg9=-2 BE][unix-seconds BE]
  [u32be(1)][12 B]; arg9 = -2 live-confirmed
- the encrypt IV is 16 plain RNG bytes (the 'IV from Spim trailing fields'
  reading was the thread-RNG pool, not the Spim)
- the spim carries 99 trailing bytes (the <=23-B cap was a probe artifact) and
  trailing[16:76] relays verbatim as the wire tail's Y group
- spim_map_signature args are (rdi=in, rsi=out) and neither its output nor the
  SHA-256(encout) digest appears on the wire — the 'two 256-bit tail fields'
  reading is falsified
- decryptPTM's math model round-trips the live PTM byte-exact (gate echo found
  at out[408])

Remaining TODO(capture): the X 32-byte group's source, the 12-byte plaintext
tail, the spim trailing's first 16 bytes. Kotlin ClearAdiFraming /
ClearAnisetteProvider restructured to the live shapes; README updated.

194 tests green (:registration:test).
2026-10-07 17:03:53 +02:00
fenleon 1b38580ad7 README: ADI anisette chain reimplemented natively; table provenance documented 2026-10-07 14:43:24 +02:00
fenleon 0c9b41e7c2 ClearADI: wire-level anisette provider — OTP derivation, provisioning, framing
Clean-room transcription of the OpenBubbles ADI white-box (unstripped Linux
.so), byte-exact against gdb-oracle vectors:

- ClearAdiOtp/ClearAdiTables: gen_otp (IOS) / gen_otp_ios (Mac) OTP ciphers,
  gen_code 2FA (SHA-1 over the 60-byte secret), 28-byte X-Apple-I-MD frame
- ClearAdiProvision: init_session, encrypt/encsec, spim_map_signature, decryptPtm
- ClearAdiFraming: asm-pinned Spim/Cpim/Ptm framing + ProvisionedData (0x78)
- ClearAnisetteProvider: GSA lookup/start/finish provisioning flow, anisette
  header map, state persistence seam
- .rodata tables embedded as base64 source (ClearAdiTablesData) — no
  Apple-derived binaries in the repo; raw bins stay untracked for dev cross-checks
- docs: spec rev 27 (OTP derivation + framing facts); unpinned wire details
  TODO(capture)

192 tests green (:registration:test).
2026-10-07 14:29:39 +02:00
fenleon 3ced0f3803 README: registration is live-validated (spec rev 26) 2026-10-05 01:00:14 +02:00
fenleon 970155a9f0 Simplify README 2026-10-05 00:57:26 +02:00
fenleon 7f960dbdfc Live-session port (2026-10-02→04): the packed courier codec (C19 closed — ALPN apns-pack-v1:4096:4096 negotiated live, packed chain anchors at the Apple Server Authentication CA intermediate, no TLS client cert on that path), the <Protocol>-span extraction for wrapped activation responses, the accepted Albert shapes (DeviceCertRequest as <data>(base64(PEM)), three-cert FairPlayCertChain), kebab-case sign-in response + client-id parsing, fresh anisette per SRP run and per 2FA call (the 434/-22421 fixes), the GSA postdata liveness client (fired AFTER the delegate sign-in, ptkn + hb-token binding), and the validation-stamp replay guard (one mint = one device record — the 409's true root cause); nullable next-hbi was already end-to-end 2026-10-05 00:22:45 +02:00
fenleon 2efd4d0f19 Add sponsor links 2026-10-04 10:28:30 +02:00
fenleon 322e24c6d2 Registration: PET fallback to the spd token map (the live-confirmed post-2FA delivery channel — the header was absent on that path); sign-in failures carry the response error shape 2026-09-29 13:25:15 +02:00
fenleon 9e00f95e3b Registration: live-verified GSA facts — au inside Status, caller-side trusted-device push (no re-trigger on validate), fresh-anisette requirement on the delegate sign-in; fixtures updated (spec rev 25) 2026-09-29 13:20:23 +02:00
fenleon e2075458b4 Spec rev 25: first live GSA login — au in Status, PET via post-2FA spd token map, trusted-device push semantics, one-time anisette, 409 ICLOUD_UNSUPPORTED_DEVICE refusal shape 2026-09-29 13:15:00 +02:00
FENN d876a9af87 Update README.md 2026-09-29 08:28:13 +02:00
FENN edd80a7e37 Update README.md 2026-09-29 08:26:59 +02:00
FENN 1c2bb0957d Update README.md 2026-09-29 08:25:23 +02:00
fenleon d6a3b515f9 Spec: restructure revision history into its own section; scrub workspace-internal paths 2026-09-29 08:16:03 +02:00
FENN 008788c408 Update README.md 2026-09-29 08:07:34 +02:00