Repository navigation
Bastion: security-hardened virtual bank sample - #6081
shai-almog wants to merge 58 commits into
Conversation
…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>
Cloudflare Preview
|
…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>
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
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>
Flutter vs Codename OneSame 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. android9 interleaved rounds; host load 0.53, 1.83, 1.62
Compute -- the VM workloads (
Gate: within tolerance against
ios9 interleaved rounds; host load 31.93, 37.8, 30.28
Compute -- the VM workloads ( 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
javascript9 interleaved rounds; host load 2.37, 1.34, 0.57
Compute -- the VM workloads (
Gate: within tolerance against
linux9 interleaved rounds; host load 0.25, 3.07, 2.63
Compute -- the VM workloads (
Gate: within tolerance against
macos9 interleaved rounds; host load 6.13, 17.14, 20.22
Compute -- the VM workloads (
Gate: within tolerance against
windows9 interleaved rounds
Compute -- the VM workloads (
Gate: within tolerance against
Codename One wins 26 of 26 measured metrics across 6 platform(s). |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 206 screenshots: 206 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows arm64Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline 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
Result: no regression |
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 192 screenshots: 192 matched. Benchmark Results
Detailed Performance Metrics
ParparVM vs HotSpot (JDK 25): macOS arm64Runner CPU: Apple M1 (Virtual) (baseline 1 performance regression:
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
A change that moves performance ON PURPOSE records the new baseline in Result: performance regression |
|
Compared 176 screenshots: 176 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
Compared 175 screenshots: 175 matched. Benchmark Results
Detailed Performance Metrics
|
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>
|
Compared 236 screenshots: 236 matched. |
…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>
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)
Client
Framework
ClipboardContent.setSensitive/setClearAfter).BigIntegerdivision andmodInversefixes,BigDecimalround/floor fixes, andEcKeys.amrclaim in the authorization server, andauth_timeleft out when unknown.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, andfloor()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