Local-first TCG pre-grading — capture, centering, forensics, AI evidence passes, deterministic grade math, certificates, and portfolio tracking.
rawgraded.com · Public assurance repository — architecture reference for security and engineering review
Submitting a card to PSA/BGS/CGC costs real money with weeks of turnaround. RawGraded Studio lets collectors pre-grade on their own machine — guided capture, PSA-style centering, optional multi-stage video forensics, local or BYOK cloud AI, and deterministic grade math before paying submission fees.
This repository is a deliberately scoped public source publication: application logic, API control patterns, and client security boundaries suitable for third-party review. Production artifacts (binaries, operator credentials, schema migration tooling, and release automation) are distributed through rawgraded.com and private release channels — not through this tree.
Created by: Joseph Edwards (@GatoGodMode) · MIT licensed
| Reader | What this means for you | Start here |
|---|---|---|
| Collectors | Pre-grade before submission fees; permanent vault record you control; certificates and portfolio exports you can trust | Product capabilities · Platform showcase · rawgraded.com |
| Underwriters & partners | Structured cost basis, provenance, receipt evidence, and exportable insurance ledger — documentation for coverage review, not a policy | Vault records · PII/SPII controls · Abuse modeling |
| Engineers & security reviewers | Public assurance source: auth boundaries, SPII strip on verify, release preflight, deterministic grading math — not a deployable secret store | Security posture · Threat model · Release integrity · Abuse modeling |
- Collectors: Know whether a card is worth grading before PSA/BGS/CGC fees. Optional hosted vault for acquisition history and certificates — local-first grading works without an account.
- Underwriters & partners: Insurance ledger PDF/print over full collections (cost basis, provenance, forensic grades). SPII stored for owner export; not published on public cert verify (
verify.php). - Engineers & security reviewers: Evaluate session auth, tenancy, server-side SPII strip, and publication boundary (
403stubs). Review grading pipeline and vault API patterns in source — binaries and operator config are out of band.
Pick your lane above; everything below is organized for depth, not linear reading.
| Principle | Implementation |
|---|---|
| Least privilege | Authenticated session required for mutating API actions and secret retrieval; admin role for operator settings |
| Secrets out of source | Third-party keys (Gemini, Stripe, PSA, market data) loaded from runtime settings — never embedded in PHP/TS |
| Local-first default | Card imagery and portfolio SQLite remain on-device unless the user opts into vault sync |
| Defense in depth | bcrypt credentials, optional TOTP, Stripe webhook HMAC, CORS allowlist, marketplace DB isolation |
| Publication boundary | This repo is source reference only; configuration and migration surfaces return 403 by design |
| Release integrity | scripts/publish-preflight.cjs enforces artifact policy before any public push |
Production-grade trust boundaries across device, hosted vault, and isolated marketplace data — designed for reviewers who need both architecture and SPII flow in one glance.
flowchart LR
subgraph device [User device]
Desktop[Electron and local SQLite]
end
subgraph vault [Hosted vault]
API[PHP session API]
MySQL[(rawgraded MySQL)]
end
subgraph market [Marketplace DB]
MktDB[(marketplace MySQL)]
end
Desktop -->|"SPII allowed owner session"| API
API --> MySQL
API -->|"isolated credentials"| MktDB
API -->|"public verify SPII stripped"| PublicQR[QR cert lookup]
- SPII allowed — authenticated owner reads/writes acquisition, tracking, notes, receipt images via session-scoped APIs (
collection.php). - SPII stripped — public cert verify by ID returns grades and identity only; acquisition fields unset in
verify.php. - Isolated — marketplace plugin uses separate DB connection (
openMarketplaceConnection()); no cross-DB credential reuse in source.
[Device] --SPII allowed--> [Hosted vault] --SPII stripped--> [Public verify]
|
[Marketplace DB] (isolated)
flowchart TB
subgraph untrusted [Untrusted]
Browser[Browser client]
Attacker[Network adversary]
end
subgraph trustDesktop [User device trust zone]
Electron[Electron main + preload IPC]
SQLite[(Portfolio SQLite)]
Ollama[Loopback Ollama optional]
end
subgraph trustHosted [Operator trust zone]
PHP[PHP vault API]
MySQL[(MySQL)]
Settings[(Runtime settings store)]
end
Browser --> Electron
Electron --> SQLite
Electron --> Ollama
Browser -.->|"HTTPS session auth"| PHP
PHP --> MySQL
PHP --> Settings
Attacker -.->|"No direct DB or settings access"| MySQL
Assumptions
- Attackers may read this repository, fuzz public endpoints, and attempt credential stuffing or webhook replay.
- Operators control hosted infrastructure, TLS termination, and MySQL hardening outside this codebase.
- Desktop users are trusted on their own machine; the Electron IPC boundary prevents renderer direct filesystem access.
Non-goals of this publication
- This tree is not a bootstrappable deployment package, migration kit, or secret store.
- Reviewers should treat absent release tooling as an intentional publication control, not an oversight.
Adversarial scenarios are modeled explicitly — the platform combines forensic capture, similarity signals, and server-side SPII boundaries so partners can audit records without mistaking user-entered fields for third-party attestation.
| Threat scenario | Engineering response | Reviewer takeaway |
|---|---|---|
| Fabricated provenance | Structured acquisition schema + optional receipt OCR; name_history audit trail |
Ledger documents owner assertions — suitable for coverage review, not notarization |
| Manipulated or resubmitted imagery | Per-scan defect evidence; front_hash/back_hash similar-scan detection (save.php, db.php); slab CV authenticity scoring (plugin_slab_checker.php) |
Forensic signals support diligence; pre-grades are not PSA/CGC certification |
| Misleading export to third parties | Owner-initiated PDF/print; SPII strip on public verify; certificate legal attribution (Certificate.tsx) |
Exports are evidence packets — authenticity of claims remains with the asset owner and reviewing party |
RawGraded optimizes for auditability and decision quality — pre-submission grading math, immutable-style cert references, and portfolio-grade exports (Platform showcase) — while keeping trust limits explicit for underwriters and security reviewers. See also Vault records.
| Class | Examples | Where it lives | In this repo |
|---|---|---|---|
| Public reference | React/Electron UI, PHP endpoint logic, grading math | GitHub | Yes |
| Operator confidential | config.php, DB credentials, signing material |
Private deploy | No |
| User confidential | Card images, portfolio rows, draft scans | Device and/or MySQL | Logic only |
| Payment confidential | Stripe secret + webhook signing keys | Settings table | Retrieval gated |
flowchart LR
subgraph client [Client tier]
Desktop[Electron Studio]
Web[Landing and legal]
end
subgraph local [Local tier]
SQLite[better-sqlite3 portfolio]
Ollama[Local AI optional]
BYOK[Gemini BYOK optional]
end
subgraph hosted [Hosted tier optional]
PHP[public/api PHP]
MySQL[(MySQL)]
end
Desktop --> SQLite
Desktop --> Ollama
Desktop --> BYOK
Web --> Desktop
Desktop -.->|"Session auth"| PHP
PHP -.-> MySQL
| Path | Role |
|---|---|
desktop/ |
Grading wizard and portfolio applications |
electron/ |
Main process, preload IPC, on-device SQLite |
components/ |
Capture, centering, forensics, vault plugins, certificates |
services/grading/ |
Deterministic grade math and consistency guards |
services/llm/ |
Gemini BYOK vs local Ollama orchestration |
public/api/ |
Hosted vault API — auth, certificates, credits, Stripe, plugins |
scripts/publish-preflight.cjs |
Pre-push policy gate |
EnVars/SysMap.xml |
Structural index for reviewers |
| Control | Mechanism |
|---|---|
| Data residency | Portfolio DB under Electron userData; session images stay in-process unless exported |
| IPC minimization | Preload exposes scoped APIs; main process owns filesystem and SQLite (electron/ipc/portfolioDb.ts) |
| BYOK cloud AI | Gemini keys supplied at runtime via settings — not compiled into bundles |
| Offline path | Full grading workflow operable without vault account |
Hosted API (public/api/)
| Control | Mechanism |
|---|---|
| Authentication | PHP sessions; requireAuth() / requireAdmin() on sensitive routes |
| Credential storage | password_hash() / password_verify() for users and admins |
| MFA | TOTP with HMAC-signed HttpOnly cookies (totp_helper.php) |
| Secret retrieval | settings_util.php + admin/session gates on settings.php |
| Transport policy | CORS allowlist on production origins (db.php) |
| Tenancy | Certificate mutations enforce user_id match or admin override |
| Payments | Stripe webhook HMAC via stripe_webhook_util.php before fulfillment |
| Marketplace isolation | Separate DB connection — openMarketplaceConnection() |
Secrets are names in settings, not values in git. Production operators configure via Admin:
| Setting key | Purpose |
|---|---|
gemini_api_key |
Cloud AI inference |
psa_public_api_key |
PSA cert lookup |
POKEPRICE_API_KEY / POKEWALLET_API_KEY |
Market data bridges |
cardhedger_api_key |
CardHedger lookup |
REMOVEBG_API_KEY |
Background removal (admin fetch only) |
stripe_* |
Checkout and webhook verification |
Public pushes are expected to pass an automated policy scan:
node scripts/publish-preflight.cjsThe gate rejects credentials, private keys, environment files, database dumps, and other artifacts that violate the publication boundary defined for this repository. Treat a failing preflight as a merge blocker, not a warning.
Operator-controlled schema evolution: DDL and migrations run through private release tooling, not this public assurance tree. public/api/sync_db.php and public/api/config.example.php return 403 by design — the same publication boundary enforced by publish-preflight.cjs.
Reference endpoints, current schema: PHP in this repo documents production logic for security and architecture review; it assumes a deployed schema and is not a bootstrap or migration surface. This separation keeps the public repo clone-safe while production moves at operator cadence.
| Layer | Capability |
|---|---|
| Desktop Studio | Electron + React grading workflow through certificate export |
| Local AI | Ollama loopback and/or Gemini BYOK |
| Grade math | Deterministic engine — evidence → computed grades, not LLM-only output |
| Hosted vault | Optional PHP API: auth, membership, archive, plugins, Stripe |
| Marketing | RAW ENGINE landing, download pages, legal/privacy |
The hosted vault web application (App.tsx grading flow + components/MyCollection.tsx collection UI) maintains structured asset lifecycle records: cost basis, source lineage, physical evidence, and exportable statements for collectors, resale counterparties, and third-party coverage review. This is distinct from the Electron local portfolio.
RawGraded is not an insurer or grading authority — ledger output is documentation for underwriter review, not a policy.
flowchart LR
subgraph capture [Capture layer]
EnvelopeScan[EnvelopeScanPlugin]
AcqWizard[AcquisitionWizard]
ManualForm[MyCollection acq form]
end
subgraph store [Vault store]
Certs[(certificates)]
Slabs[(psa_slabs)]
end
subgraph export [Export layer]
Receipt[Certificate receipt view]
Ledger[InsuranceLedger PDF/print]
ChainAPI[plugin_get_chain.php]
end
EnvelopeScan --> ManualForm
AcqWizard --> ManualForm
ManualForm -->|"collection.php update_valuation"| Certs
Certs --> Ledger
Certs --> Receipt
Certs --> ChainAPI
Slabs --> Ledger
| Concern | Implementation |
|---|---|
| Acquisition schema | 11+ persisted fields per certificate: acq_price, acq_tax, acq_shipping, acq_date, acq_source, acq_city, acq_state, tracking_number, order_id, vault_copy, user_notes, optional envelope_receipt_img — via public/api/collection.php update_valuation and public/api/save.php |
| Structured entry UX | components/AcquisitionWizard.tsx — retailer/pack catalogs, per-user presets, tax/shipping allocation, tracking + order ID |
| Lineage / copies | parent_id linking with acquisition carry-over (collection.php update_parent); chain traversal with cycle guard (public/api/plugin_get_chain.php); stats dedupe so linked copies are not double-counted |
| Audit trail | name_history JSON rendered in ledger as Asset Audit Trail (components/InsuranceLedger.tsx) |
| Per-asset receipt | Certificate internal record — investment breakdown, provenance block, forensic defect crops, QR to public cert (components/Certificate.tsx) |
| Slab provenance | PSA vault plugin stores parallel acquisition fields + controlled transfer workflow (public/api/plugin_psa_vault.php) |
Isolated OCR plugin — components/EnvelopeScanPlugin.tsx + services/geminiService.ts (extractEnvelopeData):
| Control | Detail |
|---|---|
| Pipeline isolation | Standalone from grading; failures return {} and never block the scan workflow |
| Pro-gated | Membership feature; does not consume grading credits |
| Payload discipline | Client resizes to 768px before inference; structured JSON (city, state, tracking, orderId, source, price, cardCount) |
| Origin-first extraction | Prompt prioritizes sender/store over destination — provenance-relevant metadata, reduced buyer-address capture |
| Opt-in evidence retention | “Save Image to Insurance Ledger” checkbox; receipt image persisted only when user enables (default off for users) |
| Marketplace heuristics | eBay ESUS tracking prefix normalization in App.tsx handler |
Portfolio-grade export — components/InsuranceLedger.tsx:
| Capability | Detail |
|---|---|
| Coverage scope | Includes hidden vault records (still owned assets); excludes archived; merges active PSA slabs |
| Per-asset dossier | Cost basis, provenance/source, order tracking IDs, forensic subgrades + defect count, front/back imagery, optional receipt, cert ID |
| Portfolio analytics | Collection totals; population report by set/card with cost basis aggregation |
| Export surfaces | Sectioned PDF (jsPDF + html2canvas) and print-optimized layout with isolated print CSS |
| Market data boundary | resolveMarketValue() uses unlocked market JSON only when key is in unlockedMarketKeys; otherwise falls back to estimated value |
| Statement framing | Cover title “Statement of Authenticity & Valuation” — documentation for coverage review, not insurance issuance |
Vault-records data is classified at capture and enforced at the API layer — not by UI hiding alone.
| Class | Fields | Exposure default |
|---|---|---|
| Public verification | Cert ID, card identity, grades, defect evidence, scan date, public username | Anyone with cert link / QR |
| Owner-only SPII | acq_price, acq_tax, acq_shipping, acq_date, acq_source, acq_city, acq_state |
Authenticated owner only |
| Owner-only PII / transaction SPII | tracking_number, order_id, user_notes, envelope_receipt_img |
Owner only; receipt image opt-in at capture |
| Financial SPII (gated) | market_price_json, unlocked tier pricing in ledger exports |
Owner + explicit market unlock |
Public cert verification is available by ID (QR/deep link), but acquisition and transaction SPII is stripped server-side in public/api/verify.php when the requester is not the cert owner:
// Non-owners never receive:
unset($cert['acq_price'], $cert['acq_tax'], $cert['acq_shipping'], ...);
unset($cert['tracking_number'], $cert['order_id'], $cert['user_notes'], $cert['envelope_receipt_img']);Provenance chain history for public viewers returns only non-sensitive lineage fields (id, name, overall_grade, estimated_value, date_scanned) — not acquisition columns.
| Risk | Mitigation |
|---|---|
| Cross-tenant collection access | collection.php — require_active_platform_membership(); non-admins blocked from another user's user_id |
| Cross-tenant image exfiltration | serve_image — WHERE id = ? AND user_id = ? |
| Cross-tenant chain traversal | plugin_get_chain.php — WHERE id = ? AND (user_id = ? OR admin) |
| Public verify SPII exposure | Server-side unset in verify.php (above); is_hidden removes certs from public listings while owner retains full access |
| Receipt over-collection | OCR extracts structured fields only; full image persisted only when user checks “Save Image to Insurance Ledger” |
| OCR destination-address exposure | Origin-first prompt in extractEnvelopeData — reduces capture of buyer home addresses from shipping labels |
| OCR pipeline blast radius | Isolated from grading; failures return {} — no partial SPII on error paths |
| Ledger export scope creep | Owner-initiated modal only; resolveMarketValue() respects unlockedMarketKeys |
| Custody transfer SPII bleed | PSA slab transfers — pending/resolve workflow under owner tenancy (plugin_psa_vault.php) |
| Privacy mode bulk shield | privacy.php — global privacy_mode toggles is_hidden; per-cert and bulk hide endpoints |
flowchart TB
subgraph publicView [Public verify by cert ID]
QR[QR or deep link]
VerifyAPI[verify.php]
PublicFields[Grades identity evidence]
end
subgraph ownerView [Authenticated owner]
Collection[collection.php]
Ledger[InsuranceLedger export]
FullSPII[Acquisition tracking notes receipt]
end
QR --> VerifyAPI
VerifyAPI -->|"session user_id matches owner"| FullSPII
VerifyAPI -->|"non-owner"| PublicFields
Collection --> FullSPII
Ledger --> FullSPII
Reviewer note: SPII is stored for owner documentation and export; it is not published through public verification endpoints. Insurance ledger PDFs are user-generated artifacts — treat shared exports like any exported financial document.
Enforcement summary for acquisition, provenance, and export paths:
| Control | Where |
|---|---|
| Session + membership gate | collection.php — require_active_platform_membership() |
| Tenancy on all mutations | WHERE id = ? AND user_id = ? on acquisition updates, image serve, parent linking |
| Cross-user access denied | Non-admins cannot request another user_id; admin role override only |
| Privacy layering | privacy.php — global privacy_mode, per-cert is_hidden (hidden ≠ excluded from owner ledger) |
| SPII strip on public verify | verify.php — server-side unset for non-owners |
| Chain API hardening | Auth required; visitedIds loop guard; admin-only cross-tenant read |
| Prepared statements | All acquisition and chain queries parameterized |
| Component | Path |
|---|---|
| Collection + acquisition API | public/api/collection.php |
| Provenance chain API | public/api/plugin_get_chain.php |
| Public verify + SPII gate | public/api/verify.php |
| Envelope OCR plugin | components/EnvelopeScanPlugin.tsx |
| OCR service (isolated) | services/geminiService.ts |
| Acquisition wizard | components/AcquisitionWizard.tsx |
| Collection UI | components/MyCollection.tsx |
| Insurance ledger | components/InsuranceLedger.tsx |
| Privacy controls | public/api/privacy.php |
Production UI surfaces for reviewers — scale, data governance, and engineering depth. Screenshots are from live vault data with SPII redacted where shown.
▼ Expand any section below for full-resolution gallery + engineering notes
- Sectioned export via
data-pdf-sectionchunks —components/InsuranceLedger.tsx - Progress UI during multi-page PDF assembly (
jsPDF+html2canvas) - Tested against 250+ asset portfolios without blocking the main thread
- Per-asset dossier: cost basis, provenance, forensic subgrades, optional receipt evidence
- GO PRIVATE — global archive visibility control (
privacy.php) - Public cert verify strips acquisition SPII server-side (
verify.php) — visuals match documented controls above
- Deterministic math trace + audit hash on certificates — not LLM-only grades
- Slab authenticity CV checklist (
plugin_slab_checker.php) - Three.js holo viewer with height-grid pipeline —
components/Card3DViewer.tsx
- DB path:
%AppData%/studio-portfolio/portfolio.db— WAL-mode SQLite (electron/ipc/portfolioDb.ts) - Preferences:
electron-storefor provider choice and BYOK keys — device-local - Images: Session-scoped during grading; exported via
services/export/
| Store | Purpose |
|---|---|
rawgraded |
Users, certificates, drafts, AI jobs, settings, membership |
marketplace |
Optional listing bridge — isolated credentials |
Credit queueing, Stripe fulfillment, and certificate lifecycle are implemented in public/api/ai.php, public/api/save.php, and public/api/stripe.php.
Deterministic grading combines guided capture, LLM evidence extraction, and rules-based math (services/grading/gradingMathEngine.ts). The LLM catalogs defects; the math engine assigns subgrades and PSA/BGS/CGC predictions with an auditable mathTrace.
Wizard steps: capture → crop → centering → optional video forensics → analysis → results → certificate (desktop/StudioApp.tsx).
Slab authenticity checks (services/slabAuthenticityRules.ts) are a separate track from raw-card pre-grading.
RawGraded is not affiliated with or endorsed by Pokémon, Nintendo, GAMEFREAK, Creatures Inc., PSA, BGS, Beckett, CGC, TAG, eBay, TCGplayer, GemRate, Google, Stripe, or other third parties. Names and logos are used for identification only. Pre-grading output is not an official grade from any grading company.
MIT — see LICENSE.
