bewaking-7d4e568d-ddb9

job failed

starting new job...


→ running command: dir='/etc/byggsteg/job-clone//bewaking/trunk'
branch='trunk'
if ! command -v git >/dev/null 2>&1; then
  echo 'byggsteg: git is not on this deployment PATH, so the clone cannot be updated' >&2
  exit 1
fi
if ! cd "$dir"; then
  echo "byggsteg: cannot enter the clone $dir" >&2
  exit 1
fi
root=$(git rev-parse --show-toplevel 2>/dev/null)
if [ "$root" != "$(pwd -P)" ]; then
  echo "byggsteg: $dir is not a git checkout; remove it and the next run clones it again" >&2
  exit 1
fi
if ! origin=$(git remote get-url origin 2>/dev/null); then
  echo "byggsteg: $dir has no origin remote to fetch $branch from" >&2
  exit 1
fi
before=$(git rev-parse --short HEAD 2>/dev/null) || before=none
attempt=1
while :; do
  if GIT_TERMINAL_PROMPT=0 git fetch --quiet origin "+refs/heads/$branch:refs/remotes/origin/$branch"; then
    break
  fi
  if [ "$attempt" -ge 3 ]; then
    echo "byggsteg: could not fetch $branch from $origin after 3 attempts; is the branch still there, and is the remote reachable?" >&2
    exit 1
  fi
  echo "byggsteg: fetching $branch from $origin failed; retrying in $((attempt * 2))s" >&2
  sleep $((attempt * 2))
  attempt=$((attempt + 1))
done
if ! git checkout --quiet --force -B "$branch" FETCH_HEAD; then
  echo "byggsteg: fetched $branch from $origin but could not check it out" >&2
  exit 1
fi
after=$(git rev-parse --short HEAD)
if [ "$before" = "$after" ]; then
  echo "byggsteg: $branch was already at $after"
else
  echo "byggsteg: $branch moved from $before to $after"
fi


byggsteg: trunk moved from 86178d1 to 4f34cba

→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && uname -a

Linux wolk-jjba 7.2.11-gnu #1 SMP PREEMPT_DYNAMIC 1 x86_64 GNU/Linux

→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && git log --name-status HEAD^..HEAD

commit 4f34cba6f23b75bcd7f77f6922f3384881a65921
Author: Josep Bigorra <jjbigorra@gmail.com>
Date:   Wed Nov 4 23:16:29 2026 +0100

    docs: Simplify the AI policy wording

M       README.org

→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && maak test


scanning directories for unit tests...
scanning present working directory for unit tests...

shuffled the files with seed 95242164 (replay with VERITAS_SEED=95242164)

found unit tests: (cli-spec.scm bewaking-spec.scm crypto-spec.scm web-spec.scm password-spec.scm)


running unit tests from file: cli-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 95242164 (replay with VERITAS_SEED=95242164)
+----------------------------------------------+
[📑 suite] CLI token commands
  [🧪 test] wrong keys are rejected
    [⚙️ assert] a wrong signing key exits with 1
    ✅ got: #t
  [🧪 test] key id and keyring rotation
    [⚙️ assert] a keyring round-trips and rejects the wrong key
    ✅ got: #t
  [🧪 test] token round-trip
    [⚙️ assert] generate then decrypt preserves the payload
    ✅ got: #t
+----------------------------------------------+
[📑 suite] CLI rotation metadata
  [🧪 test] token inspect audits the header without keys
    [⚙️ assert] inspect reports version, key id and expiry
    ✅ got: #t
    [⚙️ assert] inspect reports an expired envelope
    ✅ got: #t
    [⚙️ assert] inspect rejects a non-envelope token
    ✅ got: #t
  [🧪 test] expiry is sealed into tokens and enforced
    [⚙️ assert] an expired token exits with 1
    ✅ got: #t
    [⚙️ assert] --allow-expired opens it anyway
    ✅ got: #t
    [⚙️ assert] --max-age-seconds accepts fresh data
    ✅ got: #t
    [⚙️ assert] --ttl-seconds and --expires-at conflict
    ✅ got: #t
+----------------------------------------------+
[📑 suite] CLI dispatch
  [🧪 test] help and exit codes
    [⚙️ assert] help exits successfully
    ✅ got: 0
    [⚙️ assert] help prints the usage banner
    ✅ got: #t
    [⚙️ assert] help lists the token generate command
    ✅ got: #t
    [⚙️ assert] help documents the keyring option
    ✅ got: #t
    [⚙️ assert] help documents the rotation metadata options
    ✅ got: #t
    [⚙️ assert] help lists the token inspect command
    ✅ got: #t
    [⚙️ assert] version exits successfully
    ✅ got: 0
    [⚙️ assert] an unknown command exits with 1
    ✅ got: 1
    [⚙️ assert] a token subcommand is required
    ✅ got: 1
    [⚙️ assert] decrypting without keys fails
    ✅ got: 1
+----------------------------------------------+
[📑 suite] CLI object commands
  [🧪 test] object expiry policy
    [⚙️ assert] an object sealed with a past expiry is refused
    ✅ got: #t
  [🧪 test] object round-trip
    [⚙️ assert] object encrypt then decrypt round-trips a datum
    ✅ got: #t
+----------------------------------------------+
[📑 suite] CLI password commands
  [🧪 test] password strength
    [⚙️ assert] strength exits successfully
    ✅ got: 0
    [⚙️ assert] a common password is reported weak
    ✅ got: #t
  [🧪 test] password generate
    [⚙️ assert] generation exits successfully
    ✅ got: 0
    [⚙️ assert] the requested length is honoured
    ✅ got: 32
    [⚙️ assert] symbols are excluded on request
    ✅ got: (lowercase uppercase digit)
+----------------------------------------------+
[📑 suite] CLI key derivation and dropped formats
  [🧪 test] tampering and missing arguments are refused
    [⚙️ assert] an edited token exits with 1
    ✅ got: #t
    [⚙️ assert] generating without an owner id exits with 1
    ✅ got: #t
    [⚙️ assert] sealing an object without keys exits with 1
    ✅ got: #t
  [🧪 test] a refusal does not reveal which key ids the keyring configures
    [⚙️ assert] an unknown key id and an edited body give the same message and exit code
    ✅ got: #t
  [🧪 test] envelopes of dropped formats are refused
    [⚙️ assert] a BWK1 token cannot be decrypted or inspected
    ✅ got: #t
    [⚙️ assert] a BWK2 token cannot be decrypted or inspected
    ✅ got: #t
    [⚙️ assert] an empty token is refused
    ✅ got: #t
  [🧪 test] the key derivation is chosen when sealing and read back when opening
    [⚙️ assert] an Argon2id token round-trips through the CLI
    ✅ got: #t
    [⚙️ assert] a KDF parameter without --kdf is refused
    ✅ got: #t
ℹ️ info - ran 6 tasks in: 1 seconds, -100987 microseconds

running unit tests from file: bewaking-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 95242164 (replay with VERITAS_SEED=95242164)
+----------------------------------------------+
[📑 suite] Sealed envelope (versioned, authenticated)
  [🧪 test] key rotation with a keyringerror decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] a keyring seals and opens by key id
    ✅ got: #t
  [🧪 test] no reader is left for the earlier formatserror decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting object: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)
    [⚙️ assert] a BWK1 envelope is not recognised
    ✅ got: #f
    [⚙️ assert] a BWK2 envelope is not recognised
    ✅ got: #f
    [⚙️ assert] a BWK2 envelope with no expiry is not recognised
    ✅ got: #f
    [⚙️ assert] no metadata is read out of a BWK1 envelope
    ✅ got: #f
    [⚙️ assert] no metadata is read out of a BWK2 envelope
    ✅ got: #f
    [⚙️ assert] an unreadable envelope is never reported as live
    ✅ got: #f
    [⚙️ assert] a BWK2 token is refused even with the keys that sealed it
    ✅ got: #f
    [⚙️ assert] a BWK1 object is refused likewise
    ✅ got: #f
  [🧪 test] data that is not a BWK3 envelope is refusederror decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting object: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)
    [⚙️ assert] arbitrary text is not an envelope
    ✅ got: #f
    [⚙️ assert] an unknown envelope version is not recognised
    ✅ got: #f
    [⚙️ assert] a truncated envelope is refused when it is opened
    ✅ got: #t
    [⚙️ assert] recognition is a version-tag check, not a structure check
    ✅ got: #t
    [⚙️ assert] a BWK3 envelope carrying an extra MAC field is refused
    ✅ got: #t
    [⚙️ assert] an unsupported version raises rather than being guessed at
    ✅ got: #t
    [⚙️ assert] a pre-versioning bare Base16 token is refused
    ✅ got: #f
    [⚙️ assert] a bare Base16 object is refused
    ✅ got: #f
  [🧪 test] the requested key id is embedded in generated tokens
    [⚙️ assert] the token carries the key id
    ✅ got: #t
  [🧪 test] knowing the format is not knowing the plaintext
    [⚙️ assert] the plaintext, its Base16 and its Base64 do not appear in the envelope
    ✅ got: #t
    [⚙️ assert] the format is not hidden, only the content is
    ✅ got: #t
    [⚙️ assert] the public metadata is exactly what is authenticated
    ✅ got: (version key-id created-at expires-at)
  [🧪 test] the same plaintext never seals to the same value
    [⚙️ assert] a fresh salt and nonce make each envelope unique
    ✅ got: #t
    [⚙️ assert] equal objects seal to different strings
    ✅ got: #f
  [🧪 test] seal-envelope and open-envelope
    [⚙️ assert] the result is a versioned envelope
    ✅ got: #t
    [⚙️ assert] the envelope starts with the version tag
    ✅ got: #t
    [⚙️ assert] the module exposes the current format version
    ✅ got: BWK3
    [⚙️ assert] BWK3 is the only version produced and read
    ✅ got: (BWK3)
    [⚙️ assert] the plaintext round-trips
    ✅ got: top secret payload
    [⚙️ assert] a tampered envelope fails the integrity check
    ✅ got: #t
    [⚙️ assert] a wrong signing key fails the cipher check
    ✅ got: #t
    [⚙️ assert] passing a MAC key to open-envelope is refused, not ignored
    ✅ got: #t
  [🧪 test] tokens and objects are envelopes and reject tamperingerror decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] generated tokens are enveloped
    ✅ got: #t
    [⚙️ assert] sealed objects are enveloped
    ✅ got: #t
    [⚙️ assert] a tampered token is rejected
    ✅ got: #t
    [⚙️ assert] a tampered object is rejected
    ✅ got: #t
  [🧪 test] a refusal never says which key id the keyring configures
    [⚙️ assert] the refusal is a refusal, not an accidental success
    ✅ got: #t
    [⚙️ assert] an unknown key id and a tampered body fail identically
    ✅ got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] a keyring with no matching entry fails identically
    ✅ got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] a retired key fails identically
    ✅ got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] a key that is not active yet fails identically
    ✅ got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] the refusal does not name the key id it could not resolve
    ✅ got: #f
    [⚙️ assert] and it does not name a configured key id either
    ✅ got: #f
    [⚙️ assert] nor does it carry any key material
    ✅ got: #f
    [⚙️ assert] an expired genuine envelope is still told apart, since it authenticated first
    ✅ got: #f
  [🧪 test] editing a genuine envelope never yields a plaintext
    [⚙️ assert] every single-digit mutation of a real envelope is refused
    ✅ got: #t
    [⚙️ assert] and every mutation of a real sealed object too
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Generic object encryption
  [🧪 test] decrypted data is data, never code
    [⚙️ assert] Guile refuses the #. read-eval syntax
    ✅ got: #t
    [⚙️ assert] a sealed payload that looks like code stays a string
    ✅ got: #.(+ 1 2)
  [🧪 test] encrypt-object and decrypt-objecterror decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
    [⚙️ assert] sealing produces a non-empty string
    ✅ got: #t
    [⚙️ assert] round-trips an arbitrary alist payload
    ✅ got: ((user . joe) (age . 42) (roles admin editor) (tags a b) (nested (x . 1) (y . 2)))
    [⚙️ assert] fails gracefully with a wrong signing key
    ✅ got: #f
    [⚙️ assert] round-trips a plain string payload
    ✅ got: just a string
    [⚙️ assert] round-trips a number payload
    ✅ got: 1234
    [⚙️ assert] round-trips a list payload
    ✅ got: (1 2 3)
+----------------------------------------------+
[📑 suite] generate-token
  [🧪 test] works properly with correct details
owner-details: ((id . "444959d3-7a8e-455e-b608-31e35145a2fa") (email . "someone@jointhefreeworld.org") (name . "Legolas son of Thandruil") (permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))

generate-token: BWK3.default.1793831302.-.01393fb91e00b87f23bce4e1cc2fd8f1f828ec123ebfc3d022ae950fb7b0af190123552f5b7df959ac80bb752a6e138c1c2aece3ecc443044e7c35e072c5d7213fb3b1ad1e39e8f854335140bdf68e3ba78107e9c25593454337a12330cde03ee1599defe75bc9d2af36f269ff55379c9485c907d2314404dcd58652738a1cc6c0f4f78617a126232b82a51ffa04fbf078bf6ca9e3d3731ece7bdf2016f986acf660ed08e46bd71b90df5bad7eecfd64aa514fa878fee83cf7a228110f618f62a8a8053cf206687c8de61b7859704b1bcd4bbfe2e4ad4459191980fb154ee39700b83c8b8592938f34792ad1b4a4abb7b1e9b939d8475ba40e06e86b83343c1b2c29de4183f157063fc725818d03bfbebd3d5cf68bf3c99fa4fded4282c8bc14100d251220b63d7c39c9bac9cab2fa84354e84904053a56856debe403ca9101140feccdd960c8e9c89131011eec411fe0b0c79ef0cddcec72db1e10c673bacbc281f0ac5c71696709958d84f1b93d9caad54279061bcc1d4d0f0415f53ad6cf0aa2b6bc8a3ece1f9e1b731f89f12d6d09091fbfaa2d5a947c2e9c6fa56d4e5a6a6856f4a6a9e4e28204167b529f30a7b1379b1d5a8ae4f27b9951ce9e99ceb81bb94ac66df296de10ffe8861726f6b4d1c39ea370663b01c42b651bfb2b4670981dfb4bfc8e5fe358568a24e380c5c0a5c7466197de3f65f1ccd24d239092e162b32fd34f8292f84447f76b8e1a7620adbcca296fa10ec6134187a3ccdb7f79eeba10efbedd7fcddc2b5b1624dd4ffafd65f630abd16f11606d46758732804abe58b8b7faa305c9a25f3297aea15e7e0134cadeb51a5fefa42ed71b4a95f42bf97fd5da3a6332f9a8bb49e37202939277279effd3275ff4ec549d4dc17f2bc1a68333b76bb1e168f541c1963ca4a13a81fbfbd80e06243144b597a6be6cd70c7b4982c1d70dcff0f2feb60c702f0f4c91f25517cb4f4ae38bf1b6009a66a258034a42e82de7a807ed9ac559f233eb3f330b6037e7fae1d1439358a9a049b4bba97392de3349e9ecc21eee902d66e8c7db1fc52cf1636a966f11f2290add063a83c93928deea0fe61c23db6529b59273e2e02f3e5dc787d368902bb40cb576cca079ae57b597f431b29df0dab32fc56ae1260caff7aed812bb8934a56534cc46aaf87adaf6507f7d439c5b211ba574055f6c6e0addc50805605b1c52d8bbba8e0e654fa21836bb61157dab66acf6bbc0afec4f799cec183c5fbd05a127ce9042df35df658b7e4486243642f4759bb5aa3c96795a85a7df1d028

    ✅ got: #t
+----------------------------------------------+
[📑 suite] Decryption
  [🧪 test] decrypt-tokenerror decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)
    [⚙️ assert] fails gracefully when signing key is not valid
    ✅ got: #f
    [⚙️ assert] fails gracefully when token is not valid
    ✅ got: #f
    [⚙️ assert] works properly with valid token
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Token expiry
  [🧪 test] is-token-expired?
    [⚙️ assert] a token whose expiry is in the past is expired
    ✅ got: #t
    [⚙️ assert] a token whose expiry is in the future is not expired
    ✅ got: #f
    [⚙️ assert] a far-future token is not expired
    ✅ got: #f
    [⚙️ assert] a missing token-expires-at raises an error
    ✅ success catching error: misc-error: (#f is-token-expired?: Token is invalid. Missing or malformed 'token-expires-at' () #f)
    [⚙️ assert] a malformed token-expires-at raises an error
    ✅ success catching error: misc-error: (#f is-token-expired?: Token is invalid. Missing or malformed 'token-expires-at' () #f)
+----------------------------------------------+
[📑 suite] Permissions
  [🧪 test] has-needed-permissions?
    ✅ got: #f
    ✅ got: #t
    ✅ got: #f
    ✅ got: #t
    ✅ got: #t
    ✅ got: #t
    ✅ got: #f
    ✅ got: #f
+----------------------------------------------+
[📑 suite] Lossless Token Decryption
  [🧪 test] lossless-token
owner-details: ((id . "444959d3-7a8e-455e-b608-31e35145a2fa") (email . "someone@jointhefreeworld.org") (name . "Legolas son of Thandruil") (permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))

generate-token: BWK3.default.1793831302.-.017888930c5ff50c181a8aaec2a3aa99e33ee58a6dfd7249dac08967c5b6dd388bfa42148c0dfa5500d99773fdcc3dda3f52a4864eb4b00bde52cead1a71cf31d2f86732d632e676adb6f7679b0d90c1b4c0e53f218dda22ffa3b1bf5a5acda9f4c745f874f1a647025ac3fb3a056f6368e7c06325916678a9398b12f361d0289c5c3c6c106934fe6ee44060f7da3886d7deabb160eee7497998778f0dade0d8516ab0c3b5cc5898239d3f76fc7619b5fb10a7aa9adfdaa71317e15ecf19525dc83299511a6b2c5e1f3604ed0bb92edb5ce195a1fee5fde011ba4941a1d93ffc3645587be9b9a9c2f5f19105149fda8d26cc3d39378b7db21f84202caae41b3a535a104dddbce413a7078cd7dd3cda0d63be79ea2197e0b6ed7b2256a0d238627e1dc9a8fcacc4a00c0d226b930add951478bc5e66d5d8cd3a8eb6f2557eafa0413c5e5a786c5b0b872bf35ab5d8eecf7cb4c22b4ab278454c6207cacb8a6bc3ec3ecd64662286e86058565a34f00a5f20f0f3286510063882016b6a071041b3f89934b36bc88bb73cba9242301349335ec82326403f77275635b62aa583b9a1ef313ca3c73c66116b2f92537562af44e03dd8f6126ab8866bffb6c2daf3855fc1d146dc8ce38b00a830ac7bae52149fca24836e9d35624849a15c41b01147e0051c2cedd3e43923bd0ee5486e30d1e5399b843f29311e9304f97b38ea81d8ac33c4c894194380976b9698f596b9c3f1e5744f9a34faba9c678cec1bd4bd67db7c02435142b9f6b6c3245672ce5a5fc0e982dfd2684ebfa7e37d3faf5b3fd7baf5da4559107c2bf24f0c8c988b5d5d43b924f8f2f68e9ed7016aa72035e6726e56e32e2d450bdda9b4920a1c9d3733b405ccb41a3f7893b73b5776e97cc06d5e65dcf40c7919b93bc8bc9f63a3602cc340b9e849e1ef25a3f538c3219af33b9b1ee5927ac293556c0f842f657710d89bdce5c9261b6c26f1e933f24580fb697e8dec0e63c2fa29a7926370333a27ed55c4c17c22200d4aba74001fe0783032486a941c0f7620f9d1c6f473af2151e833c12b5f718ee4b264dc3175998dfe2ceab73f2fd405473dba71fdd9a7e728cf48fab01ecb942c5f41c9fcd4139008130940918d2583ee9a7179d06811514ff54836f74a125a8a0891edcfb78626baec10278f7603a1b5723db66da965a94ffe347d937fc2a569667bd2ccbb5a223bccb70c98825fd652508668534644492df49e969fe8e1ea2b5e9750c42613456f30320d142f46836e4e25d93054e1d72804c48f4adf778ee7d13a2f

decoded: ((token-type . bewaking-token) (token-id . f6671c65-80c6-4e0e-8d80-a57766c51378) (token-created-at . 1793831302) (token-expires-at . 1794436102) (owner-id . 444959d3-7a8e-455e-b608-31e35145a2fa) (owner-email . someone@jointhefreeworld.org) (owner-name . Legolas son of Thandruil) (owner-permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))

    [⚙️ assert] works properly with correct details
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Token validity helpers
  [🧪 test] token-remaining-seconds and token-valid?
    [⚙️ assert] remaining seconds is positive for a live token
    ✅ got: #t
    [⚙️ assert] remaining seconds is negative for an expired token
    ✅ got: #t
    [⚙️ assert] a missing expiry raises an error
    ✅ success catching error: misc-error: (#f token-remaining-seconds: Token is invalid. Missing or malformed 'token-expires-at' () #f)
    [⚙️ assert] a freshly generated token is valid
    ✅ got: #t
    [⚙️ assert] a freshly generated token is not expired
    ✅ got: #f
+----------------------------------------------+
[📑 suite] Envelope metadata (key id, created-at, expiry)
  [🧪 test] invalid sealing options are rejected
    [⚙️ assert] expires-at and ttl-seconds are mutually exclusive
    ✅ got: #t
    [⚙️ assert] a non-integer ttl-seconds is rejected
    ✅ got: #t
    [⚙️ assert] a key id containing a dot is rejected
    ✅ got: #t
  [🧪 test] a malformed envelope is rejected
    [⚙️ assert] a missing body is refused
    ✅ got: #t
    [⚙️ assert] a non-numeric created-at is refused
    ✅ got: #t
    [⚙️ assert] a body that is not hexadecimal is refused
    ✅ got: #t
    [⚙️ assert] a truncated body is refused
    ✅ got: #t
    [⚙️ assert] sealing a non-string plaintext is refused
    ✅ got: #t
    [⚙️ assert] sealing without a signing key is refused
    ✅ got: #t
    [⚙️ assert] an empty key id is refused
    ✅ got: #t
  [🧪 test] expired tokens and objects are refusederror decrypting token: misc-error: (#f ~A (open-envelope: the sealed data expired at 1000 (key 'default').) #f)error decrypting object: misc-error: (#f ~A (open-envelope: the sealed data expired at 1000 (key 'default').) #f)
    [⚙️ assert] a token sealed with a past expiry is refused
    ✅ got: #t
    [⚙️ assert] an object sealed with a past expiry is refused
    ✅ got: #t
    [⚙️ assert] expiry auditing ignores non-envelopes
    ✅ got: #f
  [🧪 test] metadata is reported without any key
    [⚙️ assert] created-at is a Unix timestamp
    ✅ got: #t
    [⚙️ assert] the key id is reported
    ✅ got: default
    [⚙️ assert] there is no expiry unless one was requested
    ✅ got: #f
    [⚙️ assert] the format version is reported
    ✅ got: BWK3
    [⚙️ assert] metadata is only read from envelopes
    ✅ got: #f
    [⚙️ assert] an envelope of a dropped format reports no metadata
    ✅ got: #f
    [⚙️ assert] a malformed BWK3 envelope reports no metadata
    ✅ got: #f
  [🧪 test] ttl-seconds records an absolute expiry
    [⚙️ assert] created-at is the requested timestamp
    ✅ got: 1000
    [⚙️ assert] expires-at is created-at plus the ttl
    ✅ got: 4600
    [⚙️ assert] the envelope is live before its expiry
    ✅ got: #f
    [⚙️ assert] the envelope is expired after its expiry
    ✅ got: #t
  [🧪 test] the metadata is authenticated
    [⚙️ assert] editing created-at in a BWK3 envelope breaks its tag
    ✅ got: #t
    [⚙️ assert] editing the key id breaks the tag
    ✅ got: #t
    [⚙️ assert] extending the expiry in the envelope breaks the tag
    ✅ got: #t
    [⚙️ assert] removing the expiry marker breaks the tag
    ✅ got: #t
    [⚙️ assert] relabelling the version is refused
    ✅ got: #t
    [⚙️ assert] backdating created-at breaks the tag
    ✅ got: #t
    [⚙️ assert] editing the body breaks the tag
    ✅ got: #t
  [🧪 test] expiry is enforced when opening
    [⚙️ assert] an expired envelope is refused
    ✅ got: #t
    [⚙️ assert] enforce-expiry? #f still opens it
    ✅ got: ttl payload
    [⚙️ assert] max-age-seconds enforces a rotation policy
    ✅ got: #t
  [🧪 test] keyring validity windows enforce rotation
    [⚙️ assert] a key inside its window seals and opens
    ✅ got: #t
    [⚙️ assert] a key past not-after cannot open data
    ✅ got: #t
    [⚙️ assert] a key before not-before cannot seal data
    ✅ got: #t
    [⚙️ assert] no keyring is needed to read metadata
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Garbling and Ungarbling
  [🧪 test] garble-ungarble
    ✅ got: dlrow
    ✅ got: lrow
    ✅ got: row
    ✅ got: ow
    ✅ got: w
    ✅ got: dlrow olleh
    ✅ got: hello world
    ✅ got: mxs7tu054hznj486qxtk3swcp0jwpwuy8aghnaf0vtuwt40lkp50odvfn8qtjc3hjeuwxrdjoauwx3m4jp6sxae1mo12pf8ckv1kpeosaklwqiyq89pmddiwc8ole2th
ℹ️ info - ran 10 tasks in: 4 seconds, 500932 microseconds

running unit tests from file: crypto-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 95242164 (replay with VERITAS_SEED=95242164)
+----------------------------------------------+
[📑 suite] Derived key material
  [🧪 test] an unknown derivation is refused
    [⚙️ assert] no other function can be named
    ✅ got: #t
  [🧪 test] Argon2id key material honours its parameters
    [⚙️ assert] the full key-and-IV length can be derived
    ✅ got: 48
    [⚙️ assert] changing the pass count changes the material
    ✅ got: #t
    [⚙️ assert] changing the passphrase changes the material
    ✅ got: #t
  [🧪 test] PBKDF2 key material is the PBKDF2 derivation's
    [⚙️ assert] derive-key-material agrees with the primitive it names
    ✅ got: 25ff9d4852ea4ea0ff9caf7aa33ed9e25f8d8a4eb2915445e0ff398292d67473
    [⚙️ assert] the requested length is produced
    ✅ got: 32
+----------------------------------------------+
[📑 suite] Encodings of raw bytes
  [🧪 test] Base64 round-trips arbitrary bytevectors
    [⚙️ assert] bytes that are not valid UTF-8 survive the round trip
    ✅ got: 3
  [🧪 test] Base16 round-trips arbitrary bytevectors
    [⚙️ assert] the empty bytevector round-trips
    ✅ got: #t
    [⚙️ assert] bytes that are not valid UTF-8 survive the round trip
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Base16 encoding
  [🧪 test] decoding accepts both cases
    [⚙️ assert] lowercase hex decodes
    ✅ got: hello
    [⚙️ assert] uppercase hex decodes too
    ✅ got: hello
    [⚙️ assert] encoding then decoding is lossless
    ✅ got: hello
  [🧪 test] known encodings
    [⚙️ assert] the empty string encodes to nothing
    ✅ got: 
    [⚙️ assert] lowercase hex digits are produced
    ✅ got: 68656c6c6f
    [⚙️ assert] a word encodes byte per byte
    ✅ got: 626577616b696e67
  [🧪 test] malformed input is rejected
    [⚙️ assert] an odd number of digits raises
    ✅ success catching error: misc-error: (#f bewaking: malformed hexadecimal input (odd number of digits). () #f)
    [⚙️ assert] non-hexadecimal characters raise
    ✅ success catching error: misc-error: (#f bewaking: malformed hexadecimal input. () #f)
    [⚙️ assert] embedded whitespace raises
    ✅ success catching error: misc-error: (#f bewaking: malformed hexadecimal input (odd number of digits). () #f)
+----------------------------------------------+
[📑 suite] HMAC-SHA256 (RFC 4231)
  [🧪 test] RFC 4231 test case 1
    [⚙️ assert] a 20-byte key of 0x0b over "Hi There"
    ✅ got: b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7
  [🧪 test] RFC 4231 test case 2
    [⚙️ assert] the key "Jefe" over its test message
    ✅ got: 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
  [🧪 test] the digest is 32 bytes and key-sensitive
    [⚙️ assert] 64 hexadecimal digits, i.e. 32 bytes
    ✅ got: 64
    [⚙️ assert] changing the key changes the MAC
    ✅ got: #f
+----------------------------------------------+
[📑 suite] Base64 encoding
  [🧪 test] RFC 4648 test vectors
    [⚙️ assert] empty
    ✅ got: 
    [⚙️ assert] one byte of input
    ✅ got: Zg==
    [⚙️ assert] two bytes of input
    ✅ got: Zm8=
    [⚙️ assert] three bytes of input
    ✅ got: Zm9v
    [⚙️ assert] four bytes of input
    ✅ got: Zm9vYg==
    [⚙️ assert] five bytes of input
    ✅ got: Zm9vYmE=
    [⚙️ assert] six bytes of input
    ✅ got: Zm9vYmFy
  [🧪 test] decoding is tolerant of line breaks
    [⚙️ assert] a trailing newline is ignored
    ✅ got: hello
    [⚙️ assert] encoding then decoding is lossless
    ✅ got: hello world
  [🧪 test] malformed input is rejected
    [⚙️ assert] non-alphabet characters raise
    ✅ success catching error: misc-error: (#f bewaking: malformed base64 input. () #f)
    [⚙️ assert] a truncated group raises
    ✅ success catching error: misc-error: (#f bewaking: malformed base64 input (bad length or padding). () #f)
    [⚙️ assert] data after padding raises
    ✅ success catching error: misc-error: (#f bewaking: malformed base64 input (data after padding). () #f)
    [⚙️ assert] an impossible amount of padding raises
    ✅ success catching error: misc-error: (#f bewaking: malformed base64 input (bad length or padding). () #f)
+----------------------------------------------+
[📑 suite] OpenSSL-compatible sealing
  [🧪 test] data produced by OpenSSL itself can be opened
    [⚙️ assert] an openssl-encrypted string, random salt and all
    ✅ got: a secret that openssl sealed
  [🧪 test] a fixed-salt vector matches OpenSSL byte for byte
    [⚙️ assert] the ciphertext of a known salt and key
    ✅ got: U2FsdGVkX18AAQIDBAUGByAGpafcjamTQUX3eOyvQUE=
    [⚙️ assert] and it decrypts back
    ✅ got: bewaking
  [🧪 test] failures are reported rather than ignored
    [⚙️ assert] the wrong key raises
    ✅ got: #t
    [⚙️ assert] the wrong key raises for OpenSSL-produced data too
    ✅ got: #t
    [⚙️ assert] input that is not Base64 raises
    ✅ got: #t
    [⚙️ assert] Base64 without a Salted__ header raises
    ✅ got: #t
    [⚙️ assert] a truncated body raises rather than returning garbage
    ✅ got: #t
  [🧪 test] the salt must be 8 bytes
    [⚙️ assert] a short salt raises
    ✅ success catching error: misc-error: (#f bewaking: a PBKDF2 salt must be 8 bytes long. () #f)
  [🧪 test] round-trips
    [⚙️ assert] the empty string round-trips
    ✅ got: 
    [⚙️ assert] a longer payload round-trips
    ✅ got: a secret that openssl sealed
    [⚙️ assert] one padded block follows the 16-byte Salted__ header
    ✅ got: 32
+----------------------------------------------+
[📑 suite] Key-derivation specifications
  [🧪 test] Argon2id defaults are filled in and reported
    [⚙️ assert] the defaults are 64 MiB, 3 passes, 1 lane
    ✅ got: ((kdf . argon2id) (memory-kib . 65536) (iterations . 3) (parallelism . 1))
    [⚙️ assert] given parameters are kept and the rest defaulted
    ✅ got: ((kdf . argon2id) (memory-kib . 262144) (iterations . 4) (parallelism . 1))
  [🧪 test] the default is PBKDF2 with its fixed cost
    [⚙️ assert] the module default names PBKDF2
    ✅ got: pbkdf2
    [⚙️ assert] its iteration count is the OpenSSL one
    ✅ got: 10000
  [🧪 test] unsupported specifications are refused
    [⚙️ assert] an unknown KDF name is refused
    ✅ got: #t
    [⚙️ assert] a string is not a KDF specification
    ✅ got: #t
    [⚙️ assert] a PBKDF2 cost the OpenSSL format cannot record is refused
    ✅ got: #t
    [⚙️ assert] an out-of-range Argon2id memory cost is refused
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Randomness
  [🧪 test] random-bytes returns genuinely fresh bytes
    [⚙️ assert] the default length is the OpenSSL salt length
    ✅ got: 8
    [⚙️ assert] the requested length is honoured
    ✅ got: 32
    [⚙️ assert] two calls do not return the same bytes
    ✅ got: #t
    [⚙️ assert] a draw is not a constant
    ✅ got: #f
    [⚙️ assert] a draw is not all zeroes
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Argon2id key derivation (RFC 9106)
  [🧪 test] invalid parameters are rejected
    [⚙️ assert] less than 8 KiB per lane is refused
    ✅ got: #t
    [⚙️ assert] zero passes is refused
    ✅ got: #t
    [⚙️ assert] zero lanes is refused
    ✅ got: #t
    [⚙️ assert] a non-integer memory cost is refused
    ✅ got: #t
    [⚙️ assert] a non-bytevector secret is refused
    ✅ got: #t
    [⚙️ assert] this libgcrypt provides Argon2
    ✅ got: #t
  [🧪 test] the RFC 9106 Argon2id vector is reproduced
    [⚙️ assert] password 32x01, salt 16x02, secret 8x03, data 12x04, m=32, t=3, p=4
    ✅ got: 0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659

❌ error - task raised an error: misc-error: (#f ~A (bewaking: gcry_kdf_final (Argon2id) failed: Unknown algorithm) #f)

+----------------------------------------------+
[📑 suite] Authenticated payloads (AES-256-GCM)
  [🧪 test] the tag refuses every alteration
    [⚙️ assert] a flipped ciphertext digit is refused
    ✅ got: #t
    [⚙️ assert] a flipped tag digit is refused
    ✅ got: #t
    [⚙️ assert] an unknown key-derivation id is refused, not guessed at
    ✅ got: #t
    [⚙️ assert] different associated data is refused
    ✅ got: #t
    [⚙️ assert] the wrong key is refused
    ✅ got: #t
    [⚙️ assert] a truncated payload is refused
    ✅ got: #t
  [🧪 test] fresh salt and nonce still round-trip
    [⚙️ assert] a payload sealed with random salt and nonce opens
    ✅ got: #t
    [⚙️ assert] and so does an Argon2id one
    ✅ got: #t
    [⚙️ assert] associated data is optional: empty means none
    ✅ got: #t
    [⚙️ assert] a payload sealed without it does not open with it
    ✅ got: #t
  [🧪 test] the payload is byte for byte the reference implementation's
    [⚙️ assert] PBKDF2-HMAC-SHA256, as Python's cryptography seals it
    ✅ got: 010707070707070707070707070707070709090909090909090909090915d0ed647041883a00a39c1a98153992e43396ca2ef78f30cf603552820a8ed81e320d38a87aee14ad4a7f41b2df7b369d6614500294e59e58
    [⚙️ assert] Argon2id with its parameters recorded, likewise
    ✅ got: 020000200000000001000000010707070707070707070707070707070709090909090909090909090996b424d50c29974261cc56650752a5237f7528271202d70046f0bc49c8c0bfefb86150c0b946a554a1590bec6d5f03409bbff197af7d782651
  [🧪 test] hexadecimal that stands for bytes decodes to bytes
    [⚙️ assert] base16->bytes returns a bytevector
    ✅ got: #t
    [⚙️ assert] and keeps the raw octets, which base16-decode cannot
    ✅ got: 255

❌ error - task raised an error: misc-error: (#f ~A (bewaking: gcry_kdf_final (Argon2id) failed: Unknown algorithm) #f)

+----------------------------------------------+
[📑 suite] Sealed payloads (key derivation is recorded)
  [🧪 test] sizes and headers are validated
    [⚙️ assert] an 8-byte Argon2id salt is refused
    ✅ got: #t
    [⚙️ assert] a 16-byte PBKDF2 salt is refused
    ✅ got: #t
    [⚙️ assert] a payload with no known header is refused
    ✅ got: #t
  [🧪 test] both readers keep their contracts
    [⚙️ assert] openssl-decrypt refuses what the OpenSSL CLI could not read
    ✅ got: #t
    [⚙️ assert] kdf-decrypt opens payloads produced by OpenSSL
    ✅ got: #t
  [🧪 test] tampering with the recorded parameters is not honoured
    [⚙️ assert] a changed pass count no longer decrypts
    ✅ got: #t

❌ error - task raised an error: misc-error: (#f ~A (bewaking: gcry_kdf_final (Argon2id) failed: Unknown algorithm) #f)

  [🧪 test] the default KDF keeps the OpenSSL layout byte for byte
    [⚙️ assert] a fixed-salt PBKDF2 payload equals OpenSSL's own output
    ✅ got: #t
  [🧪 test] an Argon2id payload round-trips and resists the wrong key
    [⚙️ assert] the same key opens it
    ✅ got: #t
    [⚙️ assert] another key does not
    ✅ got: #t
+----------------------------------------------+
[📑 suite] PBKDF2-HMAC-SHA256
  [🧪 test] the key and IV material OpenSSL derives
    [⚙️ assert] 48 bytes over 10000 iterations, as `openssl kdf' reports
    ✅ got: cb14e1e1f7ac2fe57f2fcaa462d4ad903867450d9778d2ccf939d8ad3cb99982e0edbd06820c0a636f6fd53d4be339fd
  [🧪 test] published password/salt vectors
    [⚙️ assert] one iteration
    ✅ got: 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
    [⚙️ assert] two iterations
    ✅ got: ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43
    [⚙️ assert] 4096 iterations
    ✅ got: c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
+----------------------------------------------+
[📑 suite] Authenticated payload hygiene
  [🧪 test] every payload uses a fresh salt and nonce
    [⚙️ assert] sealing the same plaintext twice does not repeat the payload
    ✅ got: #f
    [⚙️ assert] the recorded salts differ, so the derived keys do too
    ✅ got: #f
    [⚙️ assert] the recorded nonces differ as well
    ✅ got: #f
  [🧪 test] the salt and the nonce are used, not decorative
    [⚙️ assert] editing a salt digit breaks the tag
    ✅ got: #t
    [⚙️ assert] editing a nonce digit breaks the tag
    ✅ got: #t
    [⚙️ assert] editing an Argon2id parameter digit is refused
    ✅ got: #t
  [🧪 test] the recorded derivation cost is bounded before it is spent
    [⚙️ assert] a memory cost above the cap is refused, not allocated
    ✅ got: #t
    [⚙️ assert] a lane count above the cap is refused
    ✅ got: #t
    [⚙️ assert] a zero pass count is refused
    ✅ got: #t
    [⚙️ assert] less than 8 KiB per lane is refused
    ✅ got: #t
    [⚙️ assert] a cost inside the bounds is derived, then fails the tag: the bound is not a blanket refusal
    ✅ got: #t
  [🧪 test] an empty plaintext is still authenticated
    [⚙️ assert] the empty plaintext round-trips
    ✅ got: #t
    [⚙️ assert] even an empty payload carries a salt, a nonce and a tag
    ✅ got: #t
  [🧪 test] the associated data is authenticated but not stored
    [⚙️ assert] the associated data does not appear in the payload
    ✅ got: #f
    [⚙️ assert] one extra associated-data byte breaks the tag
    ✅ got: #t
ℹ️ info - ran 13 tasks in: 1 seconds, -171197 microseconds

❌ error - 
aborting due to error in test case...

;;; note: source file ./test/veritas/unit/cli-spec.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/test/veritas/unit/cli-spec.scm.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling ./test/veritas/unit/cli-spec.scm
;;; note: source file ./src/bewaking/cli.scm
;;;       newer than compiled /gnu/store/0w0f5c8hqiz5a524gzhhs6gakyqsjbd3-profile/lib/guile/3.0/site-ccache/bewaking/cli.go
;;; note: source file ./src/bewaking/cli.scm
;;;       newer than compiled /run/current-system/profile/lib/guile/3.0/site-ccache/bewaking/cli.go
;;; note: source file ./src/bewaking/cli.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/cli.scm.go
;;; compiling ./src/bewaking/cli.scm
;;; note: source file ./src/bewaking/bewaking.scm
;;;       newer than compiled /gnu/store/0w0f5c8hqiz5a524gzhhs6gakyqsjbd3-profile/lib/guile/3.0/site-ccache/bewaking/bewaking.go
;;; note: source file ./src/bewaking/bewaking.scm
;;;       newer than compiled /run/current-system/profile/lib/guile/3.0/site-ccache/bewaking/bewaking.go
;;; note: source file ./src/bewaking/bewaking.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/bewaking.scm.go
;;; compiling ./src/bewaking/bewaking.scm
;;; note: source file ./src/bewaking/crypto.scm
;;;       newer than compiled /gnu/store/0w0f5c8hqiz5a524gzhhs6gakyqsjbd3-profile/lib/guile/3.0/site-ccache/bewaking/crypto.go
;;; note: source file ./src/bewaking/crypto.scm
;;;       newer than compiled /run/current-system/profile/lib/guile/3.0/site-ccache/bewaking/crypto.go
;;; found fresh local cache at /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/crypto.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/bewaking.scm.go
;;; note: source file ./src/bewaking/password.scm
;;;       newer than compiled /gnu/store/0w0f5c8hqiz5a524gzhhs6gakyqsjbd3-profile/lib/guile/3.0/site-ccache/bewaking/password.go
;;; note: source file ./src/bewaking/password.scm
;;;       newer than compiled /run/current-system/profile/lib/guile/3.0/site-ccache/bewaking/password.go
;;; note: source file ./src/bewaking/password.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/password.scm.go
;;; compiling ./src/bewaking/password.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/password.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/cli.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/test/veritas/unit/cli-spec.scm.go
;;; note: source file ./test/veritas/unit/bewaking-spec.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/test/veritas/unit/bewaking-spec.scm.go
;;; compiling ./test/veritas/unit/bewaking-spec.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/test/veritas/unit/bewaking-spec.scm.go


--------------------------------------------------------------

Maak 0.14.0: the infinitely extensible command runner

Loading tasks from Maak file: /etc/byggsteg/job-clone/bewaking/trunk/maak.scm

Executing Maak tasks: test


--------------------------------------------------------------

=> Running Maak task: test

  Run the project's tests.

  Execute command:
guix time-machine --channels=channels.scm -- shell -m manifest.scm -- guile -L ./src -L ./test -c '((@ (veritas runner) run-tests))'

Executing: guix time-machine --channels=channels.scm -- shell -m manifest.scm -- guile -L ./src -L ./test -c '((@ (veritas runner) run-tests))'

maak: error: command failed (exit code 1): guix time-machine --channels=channels.scm -- shell -m manifest.scm -- guile -L ./src -L ./test -c '((@ (veritas runner) run-tests))'
        hint: Fix the command above, or run `maak --dry-run' to inspect it first.
inspect the job's lambda expression
(lambda()
  `((task . ("maak-test" "maak-deploy"))
    (project . "bewaking")
    (branch-name . "trunk")
    (clone-url . "https://codeberg.org/jjba23/bewaking.git")))