OSGAccountServer is a private backend for OSGKeyboard. - Use Kotlin 2.1+ with Ktor 3 and JDK 21. - Organize code by business feature. - Route handlers only validate and map HTTP. - Services own business rules; repositories own persistence. - Use constructor injection and explicit interfaces at external boundaries. - Never log credentials, Apple subjects, audio, prompts, transcripts, or model output. - Store monetary-like credit values as integers and use immutable ledger entries. - Require idempotency for credit grants, reservations, settlement, refunds, and StoreKit events. - Keep BYOK and local OSGKeyboard features independent from this service. - Apply SOLID, DRY, KISS, YAGNI, and OWASP guidance. - Add tests for success, failure, replay, concurrency, and boundary conditions. - Do not commit secrets or production identifiers beyond public bundle/domain names. # OSGAccountServer engineering rules - Use Kotlin 2.1.20+, Ktor 3, JDK 21 and Gradle Kotlin DSL. - Organize code by business feature under `features/`. - Keep route handlers limited to request/response mapping. - Put business rules in services and persistence in repositories. - Use constructor injection and small, testable interfaces. - Follow SOLID, DRY, KISS, YAGNI and OWASP guidance. - Never log credentials, Apple identifiers, audio, prompts, transcripts or model output. - Never store provider API keys in source control or container images. - Use integer credits. Never use floating point for balances or billing. - Credit mutations must be transactional, append-only and idempotent. - Provider failures must release reserved credits; balances must never become negative. - Sign in with Apple tokens must be verified server-side, including signature, issuer, audience, expiry and nonce. - Existing referral rewards require a qualified usage event; registration alone is not sufficient for the main reward. - Public APIs require validation, rate limiting and stable error codes. - Database migrations are immutable after release. - Tests must cover success, failure, replay, concurrency and boundary conditions. - Do not expose MySQL or internal admin routes to the public network. # OSGAccountServer - Use Kotlin 2.1+ with JDK 21, Ktor, kotlinx.serialization, Exposed, HikariCP, Koin, and Kotest. - Organize code by business feature under `features/`; keep routes thin, business logic in services, and persistence behind repositories. - Apply SOLID, DRY, KISS, YAGNI, and OWASP practices. - Store money-like credits as integer units and update balances only through immutable, idempotent ledger transactions. - Never log tokens, Apple subjects, provider credentials, audio, prompts, transcripts, or generated responses. - Keep all external providers behind interfaces and use deterministic fakes in tests. - Require tests for success, validation, replay, concurrency, timeout, and failure/refund paths. - Do not add a dependency unless the standard library or existing stack cannot solve the problem clearly. # OSGAccountServer - Use Kotlin 2.1+ with Ktor 3, JDK 21, and Gradle Kotlin DSL. - Organize code by business feature: auth, account, credits, referrals, integrity, gateway. - Keep routes limited to transport concerns; put business rules in services and persistence in repositories. - Use constructor injection and interfaces at every external boundary. - Follow SOLID, DRY, KISS, YAGNI, and OWASP guidance. - Store no audio, prompts, transcripts, or generated response bodies. - Never log secrets, Apple subjects, credentials, tokens, or user content. - Keep all credit amounts as integers. Ledger rows are immutable and all writes are idempotent. - Use database transactions and row locks for balance, reservation, settlement, and referral rewards. - Require tests for success, failure, replay, concurrency, boundary, and provider-timeout paths. - Keep public APIs versioned and document them in OpenAPI before mobile integration. - Do not commit environment files, private keys, provider credentials, or production identifiers. # OSGAccountServer - Use Kotlin 2.1.20 or newer, Ktor 3, JDK 21, and Gradle Kotlin DSL. - Organize code by business feature under `features/`. - Route handlers only validate transport input and produce responses. - Services own business rules; repositories own database access. - Use constructor injection and explicit interfaces at external boundaries. - Store money-like credits as integers and preserve an immutable ledger. - Make every externally retried mutation idempotent. - Never log tokens, Apple subjects, audio, prompts, transcripts, or model responses. - Keep provider credentials and signing keys in environment-backed secret storage. - Follow SOLID, DRY, KISS, YAGNI, and OWASP guidance. - Add tests for success, validation, replay, concurrency, timeout, and rollback paths.