Skip to content

Repository files navigation

RawGraded Studio

Local-first TCG pre-grading — capture, centering, forensics, AI evidence passes, deterministic grade math, certificates, and portfolio tracking.

License: MIT Electron React 19 Author

rawgraded.com · Public assurance repository — architecture reference for security and engineering review


Why this exists

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


For three types of readers

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 (403 stubs). 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.


Security posture at a glance

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

Threat model and trust boundaries

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.

SPII flow at a 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]
Loading
  • 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)

Detailed adversary model

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
Loading

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.

Abuse modeling and trust limits

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.


Data classification

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

Architecture

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
Loading

Repository map

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

Security controls

Desktop client

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()

Runtime secret inventory (hosted)

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

Release integrity

Public pushes are expected to pass an automated policy scan:

node scripts/publish-preflight.cjs

The 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.

Schema and migration stance

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.


Product capabilities

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

Vault records: provenance, capture, and indemnity documentation

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
Loading

Provenance capture

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)

Envelope and receipt scanner

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

Insurance ledger

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

PII, SPII, and data-leak controls

Vault-records data is classified at capture and enforced at the API layer — not by UI hiding alone.

Data taxonomy

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

Primary anti-leak gate

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.

Leak-prevention controls

Risk Mitigation
Cross-tenant collection access collection.phprequire_active_platform_membership(); non-admins blocked from another user's user_id
Cross-tenant image exfiltration serve_imageWHERE id = ? AND user_id = ?
Cross-tenant chain traversal plugin_get_chain.phpWHERE 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
Loading

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.

Vault records — security enforcement

Enforcement summary for acquisition, provenance, and export paths:

Control Where
Session + membership gate collection.phprequire_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

Vault records — source map

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

Platform showcase

Production UI surfaces for reviewers — scale, data governance, and engineering depth. Screenshots are from live vault data with SPII redacted where shown.

PDF export progress   Vault privacy mode   3D holo viewer   AI certificate   Slab authenticity   Insurance dossier   Acquisition receipt

▼ Expand any section below for full-resolution gallery + engineering notes

Expand: At scale
Insurance ledger PDF export progress over 296 assets
  • Sectioned export via data-pdf-section chunks — components/InsuranceLedger.tsx
  • Progress UI during multi-page PDF assembly (jsPDF + html2canvas)
  • Tested against 250+ asset portfolios without blocking the main thread
Expand: Trust by design
Per-asset insurance dossier with provenance and redacted receipt Vault privacy mode toggle for public archive visibility
  • 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
Expand: Engineering surface
Full grading certificate with AI reasoning and audit hash PSA slab authenticity checklist with scored verification Three.js holographic card viewer with dynamic lighting
Expand: Acquisition record
Certificate of acquisition with provenance block and verification QR

Data flows

Local portfolio

  • DB path: %AppData%/studio-portfolio/portfolio.db — WAL-mode SQLite (electron/ipc/portfolioDb.ts)
  • Preferences: electron-store for provider choice and BYOK keys — device-local
  • Images: Session-scoped during grading; exported via services/export/

Hosted vault (when enabled)

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.


Grading pipeline (summary)

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.


Third-party trademarks

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.


License

MIT — see LICENSE.

About

Redacted public showcase: local-first TCG pre-grading studio — Electron, Capacitor, PHP vault API

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages