think
16px
820px

Peruri Front-End Compliance Checklist — what it is and where Obscura stands

Source: docs/PERURI/E-SIGNATURE/[VersionDate] Front End Compliance Checklist - [ClientName]-[version].docx
(NDA — gitignored; this file only summarizes our own compliance position).
Assessed: 2026-07-17, against the worktree build including the TERA stamp slice.

What the document is for

It is Peruri Digital Security's (PDS) pre-production acceptance review of the CLIENT's
front-end
. Before they let an integration go live, a PDS QA engineer walks ~40 checklist
items across five areas, marks each PASSED/FAILED with UI screenshot evidence (the
appendix has capture sheets), and both PDS QA and their project manager sign the result.
We fill in client name, application name, portal URL and version; "PDS-CLIENT" — our
staging corporateId — is literally "a client of PDS".

The five areas: A certificate generation (registration + eKYC), B certificate
renewal, C document signing, D TERA / stempel elektronik, E JWT token usage.
Items marked Backend Checking are verified by PDS on their side from our API traffic;
the rest are UI requirements.

Compliance position by area

A. Generate certificate (items 1–14) — ✅ BUILT 2026-07-18 (staging live-proof pending)

The registration→eKYC→certificate journey is now the in-app activation wizard
(/activation), mock-proven end-to-end in a scripted browser run (including the live
camera capture). How each requirement is met:

  • Consent checkbox with Peruri's exact wording + the CA legal-page link — a mandatory
    checkbox AND a server-side hard gate (registration is rejected without it).
  • KTP upload ≤1MB (client + server enforced) with OCR autofill. Two extraction
    paths share one seam: (a) a vision-capable LLM reads the image and returns the
    fields directly (understands context); (b) for plain-text OCR engines
    (PaddleOCR, Azure Document Intelligence "Read") a label-heuristic mapper
    (KTPFieldsFromText) exploits the KTP's rigid printed layout (NIK/Nama/
    Tempat-Tgl Lahir/Jenis Kelamin/Alamat labels + a 16-digit-run NIK fallback) to
    produce the same fields. Either way OCR is a PRE-FILL the user reviews — never a
    gate; no engine at all degrades to manual entry. NIK validated as exactly 16
    digits and age ≥17 — both client-side AND re-validated server-side.
  • Email: the account email (already verified by our registration flow) IS the
    provider identity. Phone: a dedicated verification step (OTP, attempts + TTL
    capped); the registration always carries the VERIFIED number.
    Delivery rides the WhatsApp Business Cloud API (admin-configured under
    Admin → Security: phone-number ID + access token — encrypted at rest, masked in
    the UI — and an optional approved authentication template). Development falls
    back to a dev channel while unconfigured. Remaining external dependency: a Meta
    WhatsApp Business account + an approved auth template.
  • Error handling surfaces the provider's own outcomes (email/NIK already registered,
    etc.) as inline notifications.
  • Liveness video: recorded LIVE in the wizard via the browser camera
    (getUserMedia + MediaRecorder) — there is deliberately NO file-upload path. Gesture
    instructions shown (blink, open mouth, look right/left), auto-stop at 8 s, WEBM
    (spec-accepted) ≤2MB enforced client + server. Failure states render the
    "manual review / wait 1×24h" messaging; the attempt-cap state renders its own wait
    screen.
  • Specimen: pick a saved signature (canvas-drawn or uploaded PNG) or draw one in the
    wizard; the provider's 1MB cap is enforced at submission.

B. Renewal certificate (15–17) — ✅ BUILT

The wizard's expired state renders the SAME live camera capture (identical rules —
live-only, gestures, 8s, ≤2MB) and submits it to the provider's certificate-renewal
endpoint. Mock-proven end-to-end (expired → fresh video → active); staging live-proof
with the rest.

C. Signing document (18–32) — ✅ largely compliant today

  • PDF-only ≤5MB: signing paths are PDF-gated; the provider's >5MB rejection maps to a
    clean validation error. (A proactive client-side size check is a nice-to-have.)
  • Send with coordinates / order-ID handling (backend): the adapter sends the
    placement rect at send time; identical semantics proven e2e with the mock provider.
  • Drag-and-drop signature placement UI: PASSPlaceSignatureModal (drag +
    corner-resize over the rendered page).
  • Token entry UI (25–27): PASSSignOtpModal has the code input (length-agnostic,
    so 8-digit KEYLA tokens fit), a "Resend code" retry CTA, pending/progress states, and
    distinct success/wrong-code notifications.
    ⚠️ Open point: the checklist words this as KEYLA (8-digit app token) while the
    V1.4 spec still documents SMS/email OTP (and the V1.5 Postman collection has no OTP
    folder). Ask Peruri which mechanism our corporate is provisioned with; the adapter
    implements the OTP flow and has KEYLA endpoints ready.
  • ≥2 signers test (28): our envelope engine does multi-signer; the Sign-IT tier/parallel
    mapping is planned after the single flow proves out on staging. If PDS requires it for
    the review we map tier/parallel then, or demo System Signature.
  • Download signed/unsigned (29–30) and System Signature key valid/invalid (31–32)
    (backend): implemented; System-Signature session HMAC is pinned against the spec's
    worked example in a unit test.

D. TERA / stempel elektronik (33–38) — ✅ built (this session)

  • Drag-and-drop stamp placement: PASS (variant="stamp" of the placement modal).
  • Token entry UI for stamping + retry + progress (34–36): PASS — the stamp-flavored
    OTP modal; success ("Digital stamp affixed") and wrong-code failure states proven in
    a scripted browser run against the mock provider (screenshots archived).
  • Download stamped/not-stamped (backend): the shared seal completion path lands the
    stamped version (status stamped) and finalizes the stamp ledger; not-ready polls
    return cleanly.

E. JWT token (39–40) — ✅ compliant

  • Token generate with the real staging credentials works (verified live 2026-07-17).
  • "Hit the token endpoint ~once a day": the adapter caches the bearer until its
    23:59:59 WIB expiry and only regenerates on expiry/auth-class errors.

What actually goes to Peruri (data flows for area A)

Nothing is live-streamed and nothing goes browser→Peruri; every call is our backend →
Sign-IT REST API:

  • Registration form: name, NIK, date of birth, email, phone, address, org fields —
    plus the KTP photo as base64 (and NPWP photo if provided). Peruri is the CA; they
    verify the identity against their sources. Sent once, in the form-user call.
  • Liveness video: recorded live in our UI, then uploaded once as base64
    (H.264 or WEBM, ≤2MB, no MIME prefix) through our backend to the e-KYC endpoint —
    an upload, not a stream. Success auto-issues the certificate; failure falls to
    Peruri's manual review.
  • OCR: ours (client- or server-side), used only to pre-fill the form; the OCR
    artifacts themselves are not sent — Peruri receives the final field values + the
    KTP image.
  • Email/phone verification: our responsibility in our UI (Peruri's checklist
    requires that we verified them); Peruri just receives the verified values.
  • Specimen: the signature image (≤1MB PNG) is uploaded to Peruri — it's what they
    render into signed PDFs.

Bottom line

Area Status
A Generate certificate ✅ wizard built + mock-proven; needs staging live-proof + a Meta WhatsApp Business account for the (built) OTP gateway
B Renewal ✅ built + mock-proven
C Signing ✅ minus staging live-proof; KEYLA-vs-OTP to confirm
D TERA stamp ✅ built + mock-proven; staging live-proof pending
E JWT

Practical sequencing: with the wizard built, the full checklist scope (A–E) can go to
PDS review as soon as staging is unblocked and a phone-OTP gateway is wired. The KYC
gating requirement is also in: the PSrE tier is locked behind a warning + "Start
activation" prompt until the certificate is active, and stamping is blocked with
guidance until the user is TERA-registered.

Blocking item on Peruri's side (2026-07-17)

Staging credentials authenticate (token/generate → 00), but every corp-scoped call
returns 03 — User Tidak Diperkenankan
: our clientId is not yet authorized for the
PDS-CLIENT corporate (unknown corp IDs return 02, so the corp exists and the token
is fine — it's the client↔corporate binding). Until PDS activates that (and TERA/cert
quota), no registration, signing, or stamping can run on staging.