Skip to content

Bastion: security-hardened virtual bank sample - #6081

Open
shai-almog wants to merge 58 commits into
masterfrom
bastion-bank-sample
Open

shai-almog wants to merge 58 commits into
masterfrom
bastion-bank-sample

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Bastion is a virtual-bank sample in scripts/bastion, a sibling of Savor and Wayline. It shows the Codename One security stack end to end, and its multi-asset ledger can serve as the base of a crypto exchange.

Server (vm/backend)

  • Double-entry ledger: every entry sums to zero per asset, guarded balance updates, idempotency keys namespaced per owner.
  • Hash-chained audit log with a verify endpoint.
  • OAuth2/OIDC sign-in with PKCE, TOTP and recovery codes, passkeys (WebAuthn), rate limits and account lockout.
  • Every money-moving or security-changing call is signed with ES256 by a registered device key, and replays are refused.
  • Payees, cards, and an exchange with a matching engine, holds, a price collar, fees and USD-valued limits.

Client

  • Pinning and a host policy, Vault and SecureStorage, biometric and PIN lock, idle lock, device integrity, tapjacking protection, and sensitive clipboard handling.
  • A Security Center screen that reports each feature's real status. The features that need a cloud build say so.
  • Translated into English, Spanish, French, German and Hebrew (RTL).

Framework

  • Sensitive clipboard API (ClipboardContent.setSensitive / setClearAfter).
  • BigInteger division and modInverse fixes, BigDecimal round/floor fixes, and EcKeys.
  • An amr claim in the authorization server, and auth_time left out when unknown.
  • An opt-in strict biometric binding for Android SecureStorage.

Also: an Initializr template, the Bastion CI workflow, a Users guide and an Implementors guide with screenshots and diagrams, and a website page.

Behaviour change for existing apps: BigDecimal.round() now rounds half-up instead of away from zero, and floor() of a negative integer no longer subtracts one.

Not verified: real iOS and Android devices, and the cloud-build-only hardening, Play Integrity and jailbreak detection. The translations are machine translations.

🤖 Generated with Claude Code

shai-almog and others added 20 commits October 21, 2026 18:34
…ained audit log

Phase 1 of a security-showcase bank sample beside Savor and Wayline. The
ledger holds fiat and crypto alike, enforces zero-sum entries, non-negative
customer balances by conditional update, and idempotency keys; the audit log
is a SHA-256 chain appended in the transaction of the change it records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… auth

Phase 2. An OAuth 2 authorization server (PKCE public client, five-minute
access tokens, rotating refresh tokens) and a JWT resource server for /api
with roles read from the account on every request. TOTP via http.mfa with
recovery codes, passkeys via http.webAuthn, rate limits on sign-in, codes,
registration and password changes, a temporary per-account lockout, explicit
security headers, and every sign-in, refusal, enrolment and password change
appended to the hash-chained audit log. Registration opens a USD checking
account; admin-only routes verify the audit chain and the books.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d device policy

BankingApi over the ledger: own-account transfers, payments to payees with a
cooling-off period, withdrawals, simulated deposits and card spends, per-
transfer and daily limits, idempotency keys with a retry-as-lookup on a
concurrent duplicate, ES256 request signing by a registered device key
(clock skew and nonce replay refused), and an attested/rooted device policy
behind a simulated integrity verifier. Every change is audited in its own
transaction. MoneyTest 12 cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… limits, fresh sign-in for devices

Matching engine with price-time priority behind a market row lock, holds in
HOLD accounts, one balanced two-asset journal entry per trade, taker fee and
once-per-order rounding in the bank's favour, self-trade prevention, market
orders that never leave a hold. Allow-listed withdrawal addresses with
cooling-off, crypto limits valued at the index price, auth_time in access
tokens and a fresh sign-in (plus password without TOTP) to register a device.
Dev-profile market maker and price feed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…second factor and passkeys, device key and request signing, app lock, Security Center

Re-adds the common module. Every request passes one NetworkGuard (no
cleartext but the loopback dev server, a host allow-list, bundled pins,
AppShield delegated to). The device key is P-256 in portable Java (the
framework generates no EC keys and the iOS port signs RSA only), sealed in
a SecureEnvelope under a SecureStorage key, and signs the canonical form
now shared with the server (api/SignedRequest). Biometric lock on start,
resume and idle; secure screens, tapjacking blocking and clipboard
clearing; a Security Center that reports each protection honestly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-256 key generation

TDivision.divide normalized with a 64-bit leading-zero count applied to a
32-bit digit (and the overflow test in step D3 used the same), so division,
mod and modInverse answered wrong values for ordinary 256-bit inputs. The
Montgomery/Hars inverses are replaced by extended Euclid. bitCount and
bitLength(int) had the same int-vs-long bug; BigDecimal.round always rounded
away from zero and floor threw for negatives.

KeyGenerator.ecP256() generates P-256 pairs through a new
generateEcKeyPair port method (JavaSE, Android, iOS). The iOS port now loads
EC keys in sign/verify (X9.62 DER signatures, as java.security returns) and
generates them with SecKey; a PKCS#8 EC key without its public point is
completed in Java (EcKeys) before signing, since SecKey cannot import one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sfers, payees, cards, exchange, crypto withdrawals, settings, admin

Onboarding registers with the server's password rule checked as typed, signs
in keeping the server session for a passkey ceremony, enrols TOTP with a QR
code (own byte-mode level-M encoder, checked against segno for versions 1-10
and every mask) and recovery codes, offers biometric unlock as a choice and
registers the device key. Money screens sign with RequestSigner, reuse one
idempotency key per payment across retries, and turn a missing-signature
refusal into an offer to register the device. Amounts are formatted and
parsed with integer math only (money/Amounts). Bundle_en.properties lists
every key; BundleTest keeps it complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…skeys in Settings, pickers by id

DeviceKey/RequestSigner use KeyGenerator.ecP256 and Signature SHA256withECDSA
(DER to JOSE in RequestSigner); the hand-rolled P256 is gone. GET /api/assets
serves Ledger.assets and replaces the client's copied table. GET/DELETE
/api/account/passkeys plus a Settings section that adds a passkey through a
fresh kept sign-in. Pickers carry ids and make repeated labels distinct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s guides

Fixes found on PostgreSQL, MySQL and the compiled server: cancel reads the
order behind the market lock, the server refuses MySQL above READ COMMITTED,
and the exchange avoids JDK APIs the compiled class library lacks. The app's
idle timer binds to the shown form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Template.BASTION with its resources (synced from scripts/bastion), a preview
from the web e2e sign-in screen, the selector and summary entries, its README
section and brand colour, fixture and matrix checks, and check-template.sh run
by bastion.yml. Migrations (.sql) are now rewritten as text; --brand-tint and
--radius-small are set only where a template's theme declares them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…S port, amr claim for device registration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…guages, coded refusals

- security/Keys: one app key seals the device key and the refresh token
  (SealedStorage under SecureStorageTokenStore). Copies: SecureStorage's
  prompting tier (Keychain access control / Keystore user auth), a Vault
  opened by a 6-12 digit PIN (KdfProfile, peppered from the OS store,
  5 wrong tries wipe), and a plain copy only while neither exists.
- Lock/LockForm: biometric, then PIN, then sign in again; a stored session
  is read only after the unlock. Onboarding and Settings set both up.
- Security Center reports the app key, PIN, refresh token and lock honestly
  per platform.
- Android port: the biometric SecureStorage key is user-auth bound on every
  API level (was skipped on 33+) and invalidated by biometric enrolment; an
  old unbound key is replaced and its entries reported revoked.
- Server refusals carry stable codes (api/Refusal, backend Refused); the app
  maps them to error.<code> bundle keys. es/fr/de/he bundles, Lang.CODES
  restored, language choice in Settings; BundleTest checks completeness.
- Initializr template re-synced; guides updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…site page

The user's guide shows every main screen, photographed from the browser build
by run-web-e2e.sh --guide (development profile, demo accounts, device policy
off so a payee payment can be shown). The implementor's guide gains figures
for the architecture, sign-in with PKCE and a second factor or passkey, a
signed payment, a trade's ledger entry and the audit chain, and tables of
security features (with whether each needs a cloud build), settings,
endpoints and the threat model. /bastion/ page and a flagship card.

The screenshots showed two theme faults, fixed: custom label styles without a
background painted an opaque white box (the dashboard's hero total was
invisible), and the three quick-action buttons truncated their labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d device keys, idempotency key signed, locale amounts, fail-closed guard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Idempotency keys scoped per owner under a c: prefix (system keys unreachable),
device registration needs otp/mfa/hwk in amr or the password, TOTP enrolment
needs a fresh sign-in, simulated integrity verifier only on dev profiles,
atomic lockout counting, order holds not movable by customers, fiat limits in
USD across currencies, profile-row lock for every cap, single-statement ledger
verify, paged audit verify, MariaDB tx_isolation fallback, uniform payee answers.

Framework: SecureStorage.setStrictBiometricBinding opt-in (Android default is
master's again; existing keys never deleted), EcKeys public point via a fixed
256-step Montgomery ladder with complete projective formulas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tep-up for TOTP enrolment

- Core: ClipboardContent.setSensitive/setClearAfter. Display schedules the
  clear (only if nothing was copied since) through the new
  CodenameOneImplementation.clearClipboardContent. Android tags the clip
  with EXTRA_IS_SENSITIVE (API 24+ extras, honoured on 33+) and clears only
  its own clip; iOS republishes the pasteboard local only with an expiry
  date (IOSNative.restrictClipboard). SensitiveClipboardTest.
- Bastion: a reload (or restart) landed on the sign-in form because the
  start looked for the session under SealedStorage's account name while
  SecureStorageTokenStore writes under its own prefix. Session now marks
  saved sessions itself under its own key (Session.Marked);
  StoredSessionTest. run-web-e2e reload step passes.
- Bastion: adding an authenticator app after a password-only sign-in
  needs the password on start and confirm (TotpStartDto, TotpCodeDto
  password); client prompts. "Copy codes" restored via sensitive clips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nges, USD-valued limits, clipboard ownership

- AndroidImplementation.java: restore master's CRLF line endings; only the clipboard hunks differ.
- SignInGuard.recheck: in-session password checks (TOTP enrolment, device registration, device
  revoke) refuse a locked account first, count a wrong password, clear on success, and do not
  count a missing one.
- Unfreezing a card and raising its limit need a signature from a trusted device; renaming and
  removing a payee are signed; revoking a device (now POST /api/devices/{id}/revoke) needs a
  device signature or the password. Client signs all of them; docs and API table updated.
- Payee cooling-off and the step-up threshold value fiat in USD per currency.
- Card spends re-read the card behind its row lock inside the money transaction.
- Default clearClipboardContent clears only while the clipboard still holds the copied text;
  iOS resets its expiry flag on every copy; Protect's timer only forgets the digest.
- EcKeys uses modInverse (fixed on this branch); stale session marker dropped when the store is
  empty; taker-seller fee test on one-cent fills.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, fresh step-up, signed card/address/passkey changes, PIN counter in the secure store, translated Security Center

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lock, re-armed idle timer, Hebrew passkey term

- LedgerAccountRepository.frozenNow is a no-op UPDATE, so it reads the
  latest committed row and locks it on SQLite, PostgreSQL and MySQL alike.
- Lock disposes an open dialog without its closing animation and the lock
  screen shows with no transition, so the form beneath is never painted.
- Lock.tick re-arms its timer in finally.
- Comments: placeOnce is woven despite being private and self-called; the
  collar never follows fills; counting both trade sides is deliberate.
- Docs: PIN counter lives in the OS secure store; the clipboard clears
  after a minute or at the next start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

shai-almog and others added 5 commits October 23, 2026 04:23
…es minSdk to 21

Bastion's guides moved their shell blocks into docs/demos snippets; the new
core tests got the standard header and the Harmony/Bouncy Castle big-number
sources are listed as third-party; android.playIntegrity now lifts minSdk to
21, which com.google.android.play:integrity declares and the manifest merger
enforced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
restrictClipboard used to re-publish the pasteboard with LocalOnly and the
expiry after setClipboardContent had already written it normally, leaving a
window in which Universal Clipboard could sync the value. It now arms the
options and setClipboardContent's single setItems:options: carries them.
Native names are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Devices.register and SecondFactor treated amr "hwk" alone as strong, so a
passkey assertion without user verification skipped the password. Both now
need mfa/otp, or hwk together with user; MoneyTest covers hwk alone. The
implementors guide's shell blocks move to the docs/demos snippet file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e server rule

Dialog.show stretched the lone password field to ~105px, which the web e2e
does not recognise as a field (CI timed out on 'Confirm it is you'); it is
now enclosed in a BoxLayout. DeviceSetup and onboarding ask for the password
under the same rule the server applies (mfa/otp, or hwk with user) instead
of whether an app is on the account now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

shai-almog and others added 2 commits October 23, 2026 04:49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ystroke

CI typed a six-digit code that arrived as seven digits and was refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Flutter vs Codename One

Same application, built both ways: Flutter's AOT compile of the gallery, and the identical Dart source transpiled to Java on Codename One. Lower is better for every metric, so a ratio above 1.00x means Codename One is ahead by that factor.

android

9 interleaved rounds; host load 0.53, 1.83, 1.62

Metric Codename One Flutter Ratio Winner
Installed size 78.2 MB 95.8 MB 1.22x Codename One
Executable code 4.7 MB 20.6 MB 4.41x Codename One
Download size (zipped) 78.2 MB 95.8 MB 1.22x Codename One
Cold start to first frame on screen 492 ms 1663 ms 3.44x Codename One
Memory at rest 44.7 MB 86.8 MB 1.94x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 9 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 85 ms 189 ms 2.22x
longArithmetic 64 ms 58 ms 0.91x
mathTranscendental 230 ms 350 ms 1.52x
arraySequential 31 ms 36 ms 1.16x
arrayRandom 289 ms 309 ms 1.07x
objectAllocation 72 ms 51 ms 0.71x
valueEscape 8 ms 7 ms 0.88x
hashMapChurn 149 ms 310 ms 2.08x
stringBuilding 99 ms 130 ms 1.31x
recursion 89 ms 276 ms 3.10x
quicksortBench 131 ms 155 ms 1.18x
Geometric mean 1.33x

Gate: within tolerance against scripts/flutter-bench/baseline.

Android sizes are for the arm64-v8a slice a phone is delivered, on both sides; Flutter's release apk carries three ABIs and would otherwise count its engine about three times.
Android start-up and memory come from an x86_64 emulator with a software GPU. They compare the two runtimes on one machine; they are not phone timings, and a renderer that leans on the GPU is penalised more there.
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

ios

9 interleaved rounds; host load 31.93, 37.8, 30.28

Metric Codename One Flutter Ratio Winner
Installed size 108.2 MB 110.5 MB 1.02x Codename One
Executable code 17.2 MB 19.1 MB 1.11x Codename One
Download size (zipped) 80.0 MB 81.9 MB 1.02x Codename One
Cold start to first frame on screen -- -- -- not measured
Memory at rest -- -- -- not measured

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Not measured: iOS compute is not measured: a device build needs signed hardware, and on the simulator Flutter runs its JIT debug engine rather than AOT code. The macOS row runs the same ParparVM and Dart AOT compilers on the same Apple silicon.

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -7.25% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 6081 --reason "<why these moved>" baseline-ios.json, then commit the overlay it writes.

iOS start-up and memory are not measured here: Flutter cannot AOT-compile for the simulator, so a simulator run would time a JIT debug build against a release build. Sizes come from release device bundles; timings need signed hardware.
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

javascript

9 interleaved rounds; host load 2.37, 1.34, 0.57

Metric Codename One Flutter Ratio Winner
Installed size 104.0 MB 122.5 MB 1.18x Codename One
Executable code 12.9 MB 25.2 MB 1.95x Codename One
Download size (zipped) 76.3 MB 84.1 MB 1.10x Codename One
Cold start to first frame on screen -- -- -- not measured
Memory at rest -- -- -- not measured

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 552 ms wrong result Flutter: wrong result
longArithmetic 4058 ms did not run Flutter: did not run
mathTranscendental 492 ms did not run Flutter: did not run
arraySequential 190 ms 70 ms 0.37x
arrayRandom 785 ms wrong result Flutter: wrong result
objectAllocation 28 ms 21 ms 0.75x
valueEscape 92 ms 6 ms 0.07x
hashMapChurn 748 ms 201 ms 0.27x
stringBuilding 390 ms 86 ms 0.22x
recursion 392 ms 398 ms 1.01x
quicksortBench 350 ms wrong result Flutter: wrong result
Geometric mean 0.32x

Gate: within tolerance against scripts/flutter-bench/baseline; BEHIND FLUTTER on Compute (geomean of the VM workloads) (reported, not gated: a loss to Flutter on the web is currently accepted).

the JavaScript adapter needs a Chrome DevTools driver; sizes are reported and timings are not
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

linux

9 interleaved rounds; host load 0.25, 3.07, 2.63

Metric Codename One Flutter Ratio Winner
Installed size 111.5 MB 143.8 MB 1.29x Codename One
Executable code 14.1 MB 16.1 MB 1.15x Codename One
Download size (zipped) 80.9 MB 92.9 MB 1.15x Codename One
Cold start to first frame on screen 151 ms 259 ms 1.71x Codename One
Memory at rest 85.0 MB 225.6 MB 2.66x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 8 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 74 ms 183 ms 2.47x
longArithmetic 56 ms 56 ms 1.00x
mathTranscendental 253 ms 276 ms 1.09x
arraySequential 16 ms 35 ms 2.19x
arrayRandom 281 ms 234 ms 0.83x
objectAllocation 24 ms 40 ms 1.67x
valueEscape 5 ms 7 ms 1.40x
hashMapChurn 50 ms 334 ms 6.68x
stringBuilding 33 ms 111 ms 3.36x
recursion 87 ms 258 ms 2.97x
quicksortBench 138 ms 149 ms 1.08x
Geometric mean 1.84x

Gate: within tolerance against scripts/flutter-bench/baseline.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

macos

9 interleaved rounds; host load 6.13, 17.14, 20.22

Metric Codename One Flutter Ratio Winner
Installed size 124.2 MB 139.4 MB 1.12x Codename One
Executable code 33.1 MB 47.9 MB 1.45x Codename One
Download size (zipped) 85.5 MB 93.3 MB 1.09x Codename One
Cold start to first frame on screen 343 ms 534 ms 1.41x Codename One
Memory at rest 36.4 MB 54.7 MB 1.49x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 8 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 111 ms 251 ms 2.26x
longArithmetic 71 ms 67 ms 0.94x
mathTranscendental 303 ms 327 ms 1.08x
arraySequential 12 ms 45 ms 3.75x
arrayRandom 273 ms 342 ms 1.25x
objectAllocation 67 ms 31 ms 0.46x
valueEscape 5 ms 14 ms 2.80x
hashMapChurn 39 ms 187 ms 4.79x
stringBuilding 23 ms 99 ms 4.30x
recursion 129 ms 272 ms 2.11x
quicksortBench 116 ms 149 ms 1.28x
Geometric mean 1.83x

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -6.26% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 6081 --reason "<why these moved>" baseline-macos.json, then commit the overlay it writes.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

windows

9 interleaved rounds

Metric Codename One Flutter Ratio Winner
Installed size 107.8 MB 121.1 MB 1.12x Codename One
Executable code 11.4 MB 14.1 MB 1.25x Codename One
Download size (zipped) 77.9 MB 86.4 MB 1.11x Codename One
Cold start to first frame on screen 157 ms 245 ms 1.55x Codename One
Memory at rest 58.5 MB 92.6 MB 1.58x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 9 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 80 ms 183 ms 2.29x
longArithmetic 63 ms 57 ms 0.91x
mathTranscendental 198 ms 266 ms 1.34x
arraySequential 11 ms 35 ms 3.18x
arrayRandom 250 ms 373 ms 1.49x
objectAllocation 24 ms 39 ms 1.62x
valueEscape 1 ms 7 ms 7.00x
hashMapChurn 48 ms 314 ms 6.54x
stringBuilding 34 ms 121 ms 3.56x
recursion 194 ms 263 ms 1.36x
quicksortBench 138 ms 146 ms 1.06x
Geometric mean 2.17x

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -10.64%; Download size (zipped) -0.77% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 6081 --reason "<why these moved>" baseline-windows.json, then commit the overlay it writes.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

Codename One wins 26 of 26 measured metrics across 6 platform(s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 206 screenshots: 206 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 49ms / native 3ms = 16.3x speedup
SIMD float-mul (64K x300) java 49ms / native 2ms = 24.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 61.000 ms
Base64 CN1 decode 70.000 ms
Base64 SIMD encode 77.000 ms
Base64 encode ratio (SIMD/CN1) 1.262x (26.2% slower)
Base64 SIMD decode 69.000 ms
Base64 decode ratio (SIMD/CN1) 0.986x (1.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 5.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.200x (80.0% faster)
Image applyMask (SIMD off) 18.000 ms
Image applyMask (SIMD on) 20.000 ms
Image applyMask ratio (SIMD on/off) 1.111x (11.1% slower)
Image modifyAlpha (SIMD off) 18.000 ms
Image modifyAlpha (SIMD on) 15.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.833x (16.7% faster)
Image modifyAlpha removeColor (SIMD off) 20.000 ms
Image modifyAlpha removeColor (SIMD on) 9.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.450x (55.0% faster)

ParparVM vs HotSpot (JDK 25): Windows arm64

Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline windows-arm64@armv8-64-bit-family-8-model-d49-microsoft-corporation)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in vm/selfhost/perf-baseline/ is a regression and fails; one that far below is an improvement, reported but not failed (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (WinHelloMain) 4 1.00x (base 1.11x, -9.3%) 0.60x (base 0.75x, -20.4%) ok
translator (self) 4 0.84x (base 0.84x, +0.8%) 0.47x (base 0.51x, -7.2%) ok
intArithmetic 4 1.04x (base 1.04x, +0.0%) 0.06x (base 0.06x, +2.9%) ok
longArithmetic 4 0.79x (base 0.80x, -0.0%) 0.06x (base 0.06x, +3.3%) ok
mathTranscendental 4 0.72x (base 0.72x, +0.0%) 0.06x (base 0.06x, +1.5%) ok
arraySequential 4 0.36x (base 0.46x, -19.9%) 0.39x (base 0.39x, +0.0%) ok
arrayRandom 4 0.94x (base 0.94x, +0.0%) 0.23x (base 0.23x, +0.2%) ok
objectAllocation 4 1.78x (base 1.82x, -2.1%) 0.33x (base 0.43x, -23.9%) ok
valueEscape 4 0.76x (base 0.76x, +0.0%) 0.05x (base 0.05x, +3.8%) ok
hashMapChurn 4 0.99x (base 0.98x, +0.6%) 0.12x (base 0.12x, -3.3%) ok
stringBuilding 4 1.59x (base 1.43x, +11.7%) 0.27x (base 0.27x, -0.5%) ok
recursion 4 1.55x (base 1.46x, +5.8%) 0.07x (base 0.07x, +1.2%) ok
quicksort 4 1.00x (base 1.00x, +0.2%) 0.12x (base 0.12x, +0.4%) ok

Result: no regression

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • ✅ Tests: 881 total, 0 failed, 55 skipped

Benchmark Results

  • Execution Time: 10830 ms

  • Hotspots (Top 20 sampled methods):

    • 12.23% com.codename1.tools.translator.IteratorEscape.mangle (195 samples)
    • 7.65% java.lang.Object.hashCode (122 samples)
    • 7.34% java.util.ArrayList.indexOf (117 samples)
    • 6.39% java.lang.System.identityHashCode (102 samples)
    • 3.89% com.codename1.tools.translator.BytecodeMethod.equals (62 samples)
    • 3.01% java.util.AbstractCollection.containsAll (48 samples)
    • 2.88% com.codename1.tools.translator.JavascriptReachability.enqueueResolved (46 samples)
    • 2.88% java.lang.StringBuilder.append (46 samples)
    • 1.63% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (26 samples)
    • 1.57% java.lang.String.equals (25 samples)
    • 1.44% com.codename1.tools.translator.bytecodes.Invoke.findMethodUp (23 samples)
    • 1.25% com.codename1.tools.translator.Parser.cullMethods (20 samples)
    • 1.19% java.util.HashMap.hash (19 samples)
    • 1.07% com.codename1.tools.translator.IteratorEscape.ctorOnlyStoresParamsIntoThis (17 samples)
    • 1.07% java.util.HashMap.putVal (17 samples)
    • 1.00% java.io.FileOutputStream.writeBytes (16 samples)
    • 0.94% com.codename1.tools.translator.ByteCodeClass.generateCCode (15 samples)
    • 0.88% com.codename1.tools.translator.classfile.tree.JsrInliner.replay (14 samples)
    • 0.82% com.codename1.tools.translator.NativeSymbolIndex.<init> (13 samples)
    • 0.75% com.codename1.tools.translator.ReachabilityCull.run (12 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 192 screenshots: 192 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 491 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD float-mul (64K x300) java 57ms / native 2ms = 28.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 42.000 ms
Base64 CN1 decode 49.000 ms
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 51.000 ms
Image applyMask (SIMD on) 43.000 ms
Image applyMask ratio (SIMD on/off) 0.843x (15.7% faster)
Image modifyAlpha (SIMD off) 34.000 ms
Image modifyAlpha (SIMD on) 24.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off) 29.000 ms
Image modifyAlpha removeColor (SIMD on) 27.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.931x (6.9% faster)

ParparVM vs HotSpot (JDK 25): macOS arm64

Runner CPU: Apple M1 (Virtual) (baseline macos-arm64)

1 performance regression:

  • objectAllocation on all 3 CPUs: RAM 1.00x against a 0.72x baseline (+37.6%, tolerance 35%)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in vm/selfhost/perf-baseline/ is a regression and fails; one that far below is an improvement, reported but not failed (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (HelloCodenameOne) 3 0.80x (base 0.96x, -16.6%) 0.88x (base 0.93x, -4.8%) ok
translator (self) 3 0.63x (base 0.62x, +1.0%) 0.81x (base 0.78x, +3.0%) ok
intArithmetic 3 1.05x (base 1.03x, +1.5%) 0.12x (base 0.12x, +2.0%) ok
longArithmetic 3 1.03x (base 1.03x, +0.2%) 0.11x (base 0.07x, +61.7%) ok
mathTranscendental 3 0.99x (base 1.01x, -1.5%) 0.12x (base 0.12x, +0.9%) ok
arraySequential 3 0.43x (base 0.42x, +2.0%) 0.53x (base 0.53x, +0.3%) ok
arrayRandom 3 0.98x (base 0.99x, -1.1%) 0.33x (base 0.32x, +0.4%) ok
objectAllocation 3 1.11x (base 1.11x, +0.3%) 1.00x (base 0.72x, +37.6%) REGRESSION (RAM +37.6%)
valueEscape 3 0.51x (base 0.51x, +0.7%) 0.09x (base 0.09x, +1.3%) ok
hashMapChurn 3 1.11x (base 1.18x, -5.3%) 0.07x (base 0.07x, +9.9%) ok
stringBuilding 3 1.01x (base 0.84x, +19.8%) 0.46x (base 0.46x, -0.9%) ok
recursion 3 1.24x (base 1.24x, +0.6%) 0.12x (base 0.12x, +0.6%) ok
quicksort 3 0.99x (base 1.01x, -1.9%) 0.11x (base 0.11x, +1.1%) ok

A change that moves performance ON PURPOSE records the new baseline in vm/selfhost/perf-baseline/pr/6081.json, with the reason, from this job's perf-results.json. The rebaseline is then part of this pull request's diff:

python3 vm/selfhost/calibrate-perf-baseline.py --pr 6081 --reason "<why these benchmarks moved>" perf-results.json

Result: performance regression

@shai-almog

shai-almog commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 176 screenshots: 176 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1798 seconds

Build and Run Timing

Metric Duration
Simulator Boot 113000 ms
Simulator Boot (Run) 1000 ms
App Install 19000 ms
App Launch 5000 ms
Test Execution 448000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 322ms / native 4ms = 80.5x speedup
SIMD float-mul (64K x300) java 134ms / native 5ms = 26.8x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 81.000 ms
Base64 CN1 decode 75.000 ms
Base64 native encode 1741.000 ms
Base64 encode ratio (CN1/native) 0.047x (95.3% faster)
Base64 native decode 1133.000 ms
Base64 decode ratio (CN1/native) 0.066x (93.4% faster)
Base64 SIMD encode 112.000 ms
Base64 encode ratio (SIMD/CN1) 1.383x (38.3% slower)
Base64 SIMD decode 58.000 ms
Base64 decode ratio (SIMD/CN1) 0.773x (22.7% faster)
Base64 encode ratio (SIMD/native) 0.064x (93.6% faster)
Base64 decode ratio (SIMD/native) 0.051x (94.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.571x (42.9% faster)
Image applyMask (SIMD off) 420.000 ms
Image applyMask (SIMD on) 48.000 ms
Image applyMask ratio (SIMD on/off) 0.114x (88.6% faster)
Image modifyAlpha (SIMD off) 44.000 ms
Image modifyAlpha (SIMD on) 56.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.273x (27.3% slower)
Image modifyAlpha removeColor (SIMD off) 94.000 ms
Image modifyAlpha removeColor (SIMD on) 34.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.362x (63.8% faster)

@shai-almog

shai-almog commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 175 screenshots: 175 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 561 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 68ms / native 3ms = 22.6x speedup
SIMD float-mul (64K x300) java 84ms / native 3ms = 28.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 76.000 ms
Base64 CN1 decode 81.000 ms
Base64 native encode 892.000 ms
Base64 encode ratio (CN1/native) 0.085x (91.5% faster)
Base64 native decode 353.000 ms
Base64 decode ratio (CN1/native) 0.229x (77.1% faster)
Base64 SIMD encode 55.000 ms
Base64 encode ratio (SIMD/CN1) 0.724x (27.6% faster)
Base64 SIMD decode 68.000 ms
Base64 decode ratio (SIMD/CN1) 0.840x (16.0% faster)
Base64 encode ratio (SIMD/native) 0.062x (93.8% faster)
Base64 decode ratio (SIMD/native) 0.193x (80.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 86.000 ms
Image applyMask (SIMD on) 97.000 ms
Image applyMask ratio (SIMD on/off) 1.128x (12.8% slower)
Image modifyAlpha (SIMD off) 77.000 ms
Image modifyAlpha (SIMD on) 56.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.727x (27.3% faster)
Image modifyAlpha removeColor (SIMD off) 79.000 ms
Image modifyAlpha removeColor (SIMD on) 88.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.114x (11.4% slower)

shai-almog and others added 6 commits October 23, 2026 23:27
Approvals.adjust/reverse hold the maker's profile row (a no-op UPDATE,
as ProfileRepository.hold) before the idempotency lookup and the 24h
un-checked SUM, so concurrent just-under-threshold requests cannot all
post without a checker; a maker with no profile fails closed and waits.
BooksTest races eight such requests.

web-e2e.mjs: the authenticator key is re-read until it is 32 valid
base32 characters, and a refused code logs the test step, a secret
length/hash fingerprint, the server's Date skew and the last requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ad-back

The description reads null for a background app on Android 10+, so the
timed clear almost never fired. Ownership is now a per-copy token, dropped
by the clip-changed listener and re-checked on resume. Bastion: sign-safe
opening balance, and a test seam that holds the per-maker race window open
so the lock's absence fails on PostgreSQL. BigDecimal.round documents HALF_UP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y ours

A timed clear that fires while the description is unreadable (background,
API 29+) no longer clears blind; it waits for resume and runs only if the
token still matches. Resume resets the clip-change count a background
clear could leave unconsumed. Bastion's approvals test seam is now
package-private, reached through a test-only hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rocess death

Android 10+ reads the clipboard only with window focus, which arrives after
onResume; retry the pending clear from onWindowFocusChanged(true). Document
that the deferred clear does not survive process death and how Bastion covers
it. Restore getNativeThemeSettings' original line endings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…igest

The leftover check now runs after a form shows, on resume and after the
clear falls due, keeps the record while the read answers null, and skips
iOS. The digest is HMAC-SHA256 under a key in secure storage. Android:
give the focus and resume clip checks their own doc comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 236 screenshots: 236 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

shai-almog and others added 17 commits October 24, 2026 07:50
…andling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s APIs

Master regrouped the template picker and added the released-download e2e.
Bastion joins both: it is the fifth complete app, its pom gets the JUnit 5
engine, and the e2e matrix lists it. Its app calls framework API this branch
adds, so it is offered and generated only from BASTION_SINCE; until then the
shipped generator writes a NOT-OFFERED marker and e2e-shipped.sh skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fusal below it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o cut-off values

- Buttons: smaller consistent label (2.5mm) with side padding; Ui.across puts
  the theme's gap (bnButtonGapMm) between buttons and wraps a row that does
  not fit (main action on its own row) instead of shrinking or cutting labels.
- Cards: CardFace draws an ID-1 card (1.586:1, rounded, gradient, chip, brand,
  number in fours, holder, expiry, frozen overlay); details (status, type,
  account, expiry, daily limit with a used-today meter) and recent card payments.
- Landing headings wrap; the authenticator key shows whole as monospaced
  groups of four.
- Receipt: "Receipt" title with a close button; status, from, to, amount, fee,
  balance after, time, note and the full Reference wrapping.
- Statement: header with account, period, opening/closing and totals; columns
  for description, category, reference, in/out, amount and running balance,
  a column chooser, more columns on wide windows, and every row opens in full.
- PostingDto carries the entry's kind and memo for descriptions and categories.
- l10n for en/es/de/fr/he; web e2e reads the grouped key and skips read-only
  text areas; screenshots regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ongest factor

- Ui.copyable / Ui.copyButton: selectable value plus a copy icon; secrets
  (keys, account and card IDs, recovery codes) go through Protect.copy and
  clear themselves. Used for references, ledger entries and postings,
  account/card IDs, coin addresses, passkey and device key IDs, entry IDs,
  audit head hash and evidence SHA-256.
- Printout: a report laid out as monospaced lines (header, tables, totals),
  drawn on an image and printed through com.codename1.printing.Printer.
  Print on the statement, receipt, transaction, journal entry, general
  ledger, trial balance, balance sheet, income statement, audit chain,
  audit trail, access log and evidence export; no button where printing is
  unsupported. Table records its rows for printing.
- Sign-in offers passkey, then biometric unlock of the stored session, then
  password and code. The server requires user verification on passkeys,
  and SignInGuard refuses a password (and an authenticator code) for an
  account with a passkey; password plus a recovery code is the recovery
  path. AuthTest covers both; web-e2e checks the refusal end to end.
- Developer guide, five bundles and guide screenshots updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lost

A press queued 'finish editing' through a thread-spawning callSerially that
ran after the same click had opened the field, closing it again; and the
legacy editor committed on blur in a queued callback, after a button's action
had read the model. The press now finishes only the session it saw, and blur
commits the value at once. Regression check: review=selectionsignin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…age numbers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th the backend

The two primitives an Ethereum wallet needs, in portable Java so the
Bastion sample can sign chain transactions on the device or on the server
with the same class.

Hash gains KECCAK256 (original 0x01 padding, what Ethereum hashes with)
and SHA3_256 (FIPS 202, 0x06), one Keccak-f[1600] sponge in
MessageDigestImpl behind both; Hmac learns the 136-byte rate as their
block size.

Secp256k1 is written from the math, not ported from libsecp256k1: a 16x16
bit limb field whose reduction is a few bounded fold-and-carry passes, the
Renes-Costello-Batina complete projective addition (no doubling or
infinity special case to branch on), and a 4-bit fixed-window ladder with a
constant-time table scan. Everything that touches a private key or nonce
is constant time; verify and recover reuse the same code. Signatures are
RFC 6979 deterministic, low-S, and carry the recovery id; verify rejects
high-S as libsecp256k1 does, recover accepts it as ecrecover does.
Private and public tweak addition and point addition cover BIP32.

Tests: Keccak and SHA3 known answers including the NIST million-a
multi-block vector, generator multiples, the published RFC 6979 secp256k1
vectors, the Ethereum address of a published test key, recover(sign(h)),
tampering, high-S handling and tweak agreement.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- chain/: EIP-1559 signing, RLP, EIP-55, BIP-32/44 keys, JSON-RPC node
  client; per-customer deposit addresses, confirmation-depth scanning that
  stops on a reorg, sweeps, withdrawals under a lease and compare-and-set
  states, fees posted at the receipt's cost, and a reserves breaker that
  holds a withdrawal custody cannot back. Known-answer tests against
  eth-account, and run-chain-test.sh drives a geth development node (CI job).
- Audit fixes: payee removal keeps its cooling-off, a device needs a second
  factor, staff need one for the console, daily caps on new payees and
  addresses, rate limits per principal, production refuses default secrets,
  expired nonces purged, ETH addresses checked by EIP-55.
- Console custody panel, Receive crypto screen with an EIP-681 QR code.
- UI: chips instead of stretched badges, gaps between console buttons and
  charts, a spinner with skeleton cards while loading; the web e2e waits
  for content before each screenshot.
- Initializr e2e: a template is offered only from its release floor, and
  the pinned versions must be releases, never snapshots.
- Implementor's guide: on a real chain, and a security checklist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…date

- Entities hold java.time.Instant timestamps (same BIGINT columns, no
  migration); repository time parameters are Instant, DTOs stay epoch ms.
- AppAttestVerifier: an iPhone's App Attest attestation is verified on the
  server with the backend PKI -- chain to Apple's root, the nonce bound to
  the device key being registered, app id, counter and environment --
  when bastion.appattest.app-id is set.
- Skill: Bastion in the demo apps reference; backend reference covers
  exception handlers, advice, validation, interceptors, java.time in the
  ORM, streaming bodies and HttpProxy, PKI, ECDH, Keccak and secp256k1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd and in the template

String.split is not in the compiled backend's class library, and the
template's rename rewrote the test's app id away from its fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant