# SHD.build architecture This project is intentionally being built in vertical slices. ## Planned stack - SvelteKit 2 / Svelte 5 with TypeScript - Embedded Turso Database via `@tursodatabase/database` for durable application data - Auth.js-compatible server authentication with Discord OAuth and credentials providers - Argon2id password hashing, server-side sessions, CSRF protection, and role checks - Browser `localStorage` only for anonymous build drafts ## Product boundaries - Public: browse, sort, and view published builds and guides. - Anonymous: create and edit local drafts. - Authenticated: publish, rate, comment, favorite, copy, and manage owned builds. - Editor: manage builder source data. - Admin: manage users, roles, source data, and audit history. Authorization must be checked on the server for every mutation. Hiding an admin link is not authorization. Local credentials are stored as Argon2id PHC strings with a unique random salt per password. Current work factors follow OWASP's 19 MiB / 2 iteration / single-lane minimum profile and can be increased over time; successful login will eventually rehash credentials whose parameters are outdated. ## Database runtime The application uses Turso Database in-process through its native Node package. It opens a local SQLite-compatible database file and does not use Turso Cloud, libSQL HTTP, remote replicas, auth tokens, or synchronization. The database defaults to `./data/shd-build.db`. Production deployments must provide a persistent writable filesystem and can override the location with `TDR_DATABASE_PATH`. This rules out ephemeral serverless filesystems unless a persistent volume is attached. ## Increment plan 1. Product shell and anonymous local-draft builder. 2. Embedded Turso migrations, repositories, and seed data. 3. Credentials and Discord authentication. 4. Full equipment/stat builder driven by database definitions. 5. Publishing, discovery, ratings, comments, favorites, and copy flow. 6. Rich build guides and moderation. 7. Admin editors, validation, audit log, and data import/export. # Administrator bootstrap The first successfully authenticated user is promoted to `admin` if the database does not contain an administrator. Promotion uses one conditional database update so concurrent first sign-ins cannot both become administrators. Once an administrator exists, later sign-ins never alter roles. # Database backup and restore Embedded Turso databases contain contributor-entered source data and must be preserved across development changes. Create a timestamped development backup: ```sh npm run db:backup ``` Create a production backup: ```sh npm run db:backup:production ``` The scripts write versioned JSON documents beneath `data/backups/` by default. That directory is ignored by Git because backups contain private user data, including password hashes. To restore, first create an empty database and apply the schema. Then run: ```sh npm run db:restore -- data/backups/.json ``` The restore operation refuses to import into any database containing records. This guard prevents accidental merging or overwriting of contributor data. Stop the application before backup or restore so the embedded database file is not locked by another process. # ISAC audit and compensating reversals Every successful ISAC mutation appends an event to `audit_log` through the shared server audit utility. Events identify the operator, operational category, specific action, target entity, timestamp, and useful before/after evidence. The audit registry is read-only and requires `audit.view`; the seeded Auditor role grants that permission without granting mutation capabilities. Audit history is immutable. “Revert” is implemented as a new compensating mutation rather than deletion or modification of the original event: 1. The operator must independently hold `audit.revert`. 2. The original action must be explicitly supported and contain a usable before-snapshot. 3. The UI requires a second, explicit confirmation. 4. The server reloads the target and compares it with the original after-snapshot, ignoring bookkeeping timestamps. A mismatch means newer work exists and blocks automatic compensation. 5. Referenced roles, permissions, and relationships are revalidated before restoration. Restoring administrator authority additionally requires full administrative access. 6. The server restores the previous state and appends an `audit.reverted` event whose target is the original audit-event ID. # Moderated game-data contributions ISAC data access is split into `data.view`, `data.propose`, `data.manage`, and `data.approve`. Data Viewers can inspect registries. Data Contributors can submit the same editor forms, but the request is serialized into `data_change_proposals` instead of mutating canonical tables. Data Oversight reviews the queue and must explicitly confirm approval or rejection. Approval replays the stored form through its original SvelteKit server action. This preserves canonical validation, relational updates, icon processing, and the normal game-data audit event. Proposal creation and review decisions also produce separate `data_moderation` audit events. Failed canonical validation marks a proposal as failed without changing live data. Pending counts appear alongside each data registry in ISAC so collaborators can see concurrent suggestions. The protected seeded roles are Data Viewer, Data Contributor, Data Editor, and Data Oversight. Data Editor retains direct-write access for trusted operators; Data Oversight combines direct-write access with queue review. New ISAC data modules must use the shared moderation interception path and audit both the final mutation and proposal lifecycle. Every authenticated, active registered agent receives `isac.access`, `data.view`, and `data.propose` as baseline permissions in the authorization layer. This baseline is intentionally implicit rather than a removable role assignment, so it applies consistently to existing users and every account creation method. Registered agents can inspect live source data and submit moderated proposals, but cannot directly mutate canonical records, review the queue, or access unrelated ISAC operations without additional permissions. 7. A successfully reverted event cannot be reverted again. Unsupported destructive identity operations, account deletion, incomplete snapshots, stale targets, and events whose dependencies no longer exist are marked non-reversible or rejected. Recovery for those cases requires a specialized workflow rather than inferred database writes. # System metrics The read-only `/admin/metrics` route requires `metrics.view` and collects a point-in-time process-local health snapshot. Node and operating-system APIs provide process CPU and memory, event-loop utilization and delay, host memory, load average, core count, platform, and uptime. Embedded Turso pragmas provide page allocation, page size, and free-page information; aggregate SQL queries provide application record volumes. The interface refreshes on demand and may poll every ten seconds while the operator keeps auto-refresh enabled. CPU and event-loop values are deltas from the previous server-side sample, so the first or infrequently requested sample is directional rather than a monitoring-grade time series. Production alerting and historical analysis should eventually use a dedicated external telemetry system. This surface deliberately omits secrets, environment-variable values, and full database paths. # Weapon-owned exotic talents Standard weapon talent pools remain globally reusable and classified by Talent 1 / Top or Talent 2 / Bottom. An exotic talent is different: one exotic weapon owns one dedicated talent, referenced by `weapons.exotic_talent_id`. The weapon editor creates and updates the underlying normalized `talents`, `talent_tiers`, trigger links, effect links, description template, and Exotic weapon scope together with the weapon. Server validation rejects an exotic talent ID not already owned by the edited weapon and rejects any talent shared by another weapon. Converting a weapon back to standard clears the relationship and archives its now-unowned exotic talent. Exotic talent creation and editing are rejected by the shared Talent Registry so ownership cannot be bypassed. # OS Protocol progression OS Protocols use dedicated normalized tables because their talent progression has six calculated states (base plus five upgrades) rather than the shared two-tier talent model. Each protocol owns one `os_protocol_primary_effects` record and one `os_protocol_talents` record. Talent effects and reusable triggers live in their respective join tables. The three secondary attributes are normalized separately. One `os_protocol_secondary_profiles` record exists for each configured quality + attribute-type combination, with its three fixed stat/value pairs in `os_protocol_secondary_effects`. Every matching protocol inherits that profile; secondary values are not copied onto individual protocol records. The unique primary value uses `base × (1 + (level − 1) / 49)` for levels 1–50, while talent effects use `base × (1 + upgrades / 5)` for zero to five upgrades. Server actions validate all relationships and append the full owned graph to the existing `game_data` audit history. # Skill Chip quality scaling Skill Chip sets support Superior and High-end chip instances. Each set owns one 2-piece bonus, distinct primary and secondary stat-option pools, and one 3-piece talent with reusable trigger links and normalized effect rows. Primary and secondary options reference active attributes that define Skill Chip minimum and maximum roll values; a configured chip rolls one primary and two distinct secondary stats from those separate pools. Both set-bonus progression systems use the number of equipped High-end chips as the interpolation step. For `h` High-end chips, where `h` is zero through three, a configured minimum and maximum resolve as `minimum + (maximum − minimum) × h / 3`. Zero High-end chips therefore produce the minimum, while three produce the maximum. The editor stores only endpoints and previews all four states, preventing duplicated tier data from diverging. Skill Chip mutations, owned relationships, and safe icon metadata are captured in the `game_data` audit stream. # Game-data icon storage Talents, brand sets, OS Protocols, and Skill Chips reference uploaded artwork through the polymorphic `game_data_icons` metadata table. Original uploads are never retained. The shared server pipeline accepts transparent SVG, PNG, WebP, and AVIF sources, rejects active or externally referenced SVG content, and uses Sharp’s asynchronous native worker pool to rasterize and normalize every accepted icon to a transparent 256 × 256 WebP. Normalized files live beneath the ignored `data/game-icons/` directory and are served by the validated `/icons/game-data/[type]/[id]` endpoint with immutable cache headers and MIME sniffing disabled. ISAC save actions process files before mutation, apply upload or removal after record validation, and include only safe icon metadata—not image bytes—in their audit snapshots.