Skip to content
ADR-0033Architecture decisionAcceptedversion 1.0.02 October 2026

Plain summary ​

When a store reviewer opens the app, the identity verification step asks for a real Turkish ID card and a face scan; the reviewer has neither, and the app is rejected as "could not be tested" (the most common rejection reason for identity apps). Decision: a short-lived, single-use code written into the review notes. Once the code is entered, the identity service uses the fake (demo) verification instead of the real provider for that session only, and issues a test credential marked "DEMO" that no real verifier accepts. Real users never see this path; the code is spent after one use.

Context ​

  • Apple App Review and Google Play review require every flow of the app to be testable; where needed a test account or a "demo mode" is provided in the review notes.
  • Tamga Wallet's identity flow (SPEC-ID-0003 §9) requires a real identity document + liveness + face matching (kimlik doğrulama (identity proofing)Belge verilmeden önce kişinin gerçekten o kişi olduğunun doğrulanması; örneğin kimlik kartı ve canlılık testiyle.). In production the fake provider is off (TAMGA_IDV_DIDIT_FAKE only if explicitly enabled; GT1: in production a missing real identity is never treated as present).
  • Flows that need no identity can be tried without a code: buying a ticket and showing it at the gate, the Tamga Verify example policies, the "Sign in with Tamga" example site (the takma ad (pseudonym)Cüzdanın her web sitesi için ayrı türettiği kararlı hesap kimliği; iki site aynı kişiyi eşleştiremez. works only with the seed from the identity belge (credential)Belge verenin imzaladığı ve kişinin cüzdanında duran dijital belge; kişi yalnız istenen alanları gösterir., so that too depends on the identity flow — §K4).
  • A path is needed that does not affect real users, cannot be abused and is traceable.

Options ​

OptionResultWhy
A. Only identity-free paths in the review notesnot enoughThe identity flow is the app's main path; if the reviewer cannot try it, the rejection risk is high.
B. A permanent "demo mode" in the app (fake credentials on the device)rejectedReal users could enable it too; fake credentials might look real; the store may treat it as a "hidden feature".
C. A pre-prepared test wallet / devicenot feasibleThe reviewer installs on their own device; keys are bound to the device (WL1).
D. Time-limited, single-use review code → fake provider + separate test signeracceptedOnly someone with the code can use it; the credential does not pass in the real ecosystem; every use is logged.

Decision ​

K1 — The code ​

  • The operator generates the code on the server (an ops script; not from the console): random, at least 128 bits, in a readable form (e.g. 4×5 characters).
  • Only a digest of the code is stored (HMAC with a service key); the plain code is never stored or logged.
  • Validity: at most 14 days; single-use (spent on the first successful identity flow); at most 3 active codes at a time.
  • The code is written into the store review notes and shared nowhere else.

K2 — Effect of the code (that session only) ​

  • The wallet does not show an "I have a review code" link at the start of the identity flow; the code is entered into a small "Review code" field on the privacy notice screen of the /authorize page (the real user flow does not change).
  • With a valid code the identity service uses the fake provider for that PAR (Pushed Authorization Request)Yetkilendirme isteğinin önce sunucuya gönderildiği OAuth adımı; içerik tarayıcı adresinde görünmez. (FakeIdvProvider; ready-made test persons); no request goes to the real provider.

K3 — The test credential does not pass in the real ecosystem ​

  • The credential is signed not with the identity service's real key but with a separate test signer. The test signer sits in the güven listesi (trust list)Bir ülkenin kök sertifikalarını, belge verenlerini ve kayıtlı relying party'lerini taşıyan imzalı liste. Bugün Tamga'da güven bu listelere dayanır; ortak defter sonra gelir. as a separate DEMO record; only the "review" policies in Tamga Verify accept it.
  • The credential carries verification_method: "review-demo"; the name fields explicitly contain "DEMO"; exp is at most 7 days.
  • Institutional belge veren (issuer)Belgeyi imzalayıp veren kurum: üniversite, meslek kuruluşu, kamu kurumu ya da şirket.s (universities etc.) do not accept the test signer's credential for identity matching (trust list policy).
  • The pseudonym seed is derived with the test signer and from the test key; it never collides with real seeds.

K4 — What the reviewer can try ​

With the code: identity verification → identity credential → "Sign in with Tamga" example site (pseudonym) → Tamga Verify "over 18" example → reset the wallet and delete my data. Without the code: buying a ticket and showing it at the gate, the verification policies page.

K5 — Trace ​

Every use of a code is written to the event log (review_code.used; the code id, not the code digest); test credentials are counted separately and appear in the transparency report as "store review".

Rationale / alternatives ​

See the options table. D is the only option that lets the reviewer try every path without changing the real user flow and without leaking fake credentials into the real ecosystem. The cost: a second signer in the identity service and a DEMO record in the trust list.

Risks ​

RiskMitigation
The code leaks and someone else uses itsingle-use, time-limited, at most 3 active; the test credential does not pass with a real verifier
The test credential is mistaken for a real oneseparate signer, DEMO marking, "DEMO" badge in the wallet, 7 days
The store treats it as a "hidden feature"explained openly in the review notes; the code field is visible on the notice screen, not hidden
The fake provider is accidentally opened to everyone in productionthere is no path to the fake provider without a code; TAMGA_IDV_DIDIT_FAKE stays off in production

Invariants ​

CodeRule
RV1A review code is stored only as a digest, is time-limited (≤ 14 days) and single-use; it is never written to the log in plain form.
RV2A credential issued with a review code is not signed with the identity service's real signer and passes only the review policies.
RV3Without a review code the production identity service cannot switch to the fake verification provider.

Implementation plan ​

StepWhereWork
1apps/id (operator repository)code table (digest, expiry, used), /authorize code field, provider choice per PAR, test signer
2ops (operator repository)review-code.ts (create / list / revoke)
3tamga-network trust listDEMO test signer record; the institution issuers' matching policy excludes it
4apps/verify"review" policies (accepting the test signer)
5Tamga Wallet (separate repository)"DEMO" badge (credential verification_method: review-demo)
6DocumentationSPEC-ID-0003 §9, store review notes

Estimated effort: 2–3 days (with tests).

Implementation ​

  • Code: apps/id/src/review.ts (operator repository) (26 characters Crockford base32 = 130 bits; HMAC digest with a separate sub-key derived by HKDF from the document-digest key; active codes compared in constant time); table review_codes; operator tool ops/review-code.ts create | list | revoke (operator repository). The code is spent on the first successful issuance; while a flow is in progress it is not given to another session. Limits: 5 failed attempts per PAR, a 15-minute lock after 20 failures in 10 minutes service-wide (no IP — G2); nginx rate limit on /authorize/consent.
  • Flow: a collapsed "Review code" detail on the /authorize notice page; with a valid code /review-idv/{session} (a DEMO person specific to the code). The code-less flow and /fake-idv do not change (RV3; in production /fake-idv/ is closed in nginx).
  • Signer: ops/pki/issuer-id-review (dev PKI; the certificate in the repository, the private key to the server with upload.ps1 -WithPki). The trust list record tamga-id-review (IDENTITY, EAA (Electronic Attestation of Attributes)Kişinin bir özniteliğini (diploma, üyelik gibi) doğrulayan belge için eIDAS'taki ad., I1; status list key is the identity service's) is a pending application in apps/trust-publisher/registry/pending/: because ADR-0024 requires the institution's identifier and address for new records, it is added with register issuer once Tamga's details are entered. Until then the review path answers "not enabled" (503).
  • Verification: Tamga Verify review-age-over-18 and review-site-signup (I1); "Sign up with a DEMO credential" on the example site. Every real policy requires I2 → the DEMO credential is REJECTED at E1 (test: packages/verifier/src/review-demo.test.ts).
  • Schema: review-demo added to the verification_method values (development stage, ADR-0029); FW-RB-0003 (Identity Rulebook) is written accordingly.
  • Wallet: a "DEMO — app review credential" card in the credential details.

Resolved questions (project management, 2026-10-01) ​

  1. Option D accepted.
  2. The code field is on the identity service's notice (consent) page.
  3. The test credential is valid for 7 days.

Status ​

Accepted — 2026-10-01 (approved by project management: all three questions accepted). Implementation: the plan above.