id: 4a7fcbf3efca4cd7a12aabd5d10a2c45
parent_id: 
item_type: 1
item_id: 980c3eb587294e4383474b94988f2f88
item_updated_time: 1785523646817
title_diff: "[{\"diffs\":[[1,\"Skara Brae — Tech Stack & Architecture\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":38}]"
body_diff: "[{\"diffs\":[[1,\"# Skara Brae — Rust Backend Architecture\\\n\\\n## Why This Stack in 2026\\\n\\\nTorn launched in 2004 as a PHP/HTML text game. It received an engine overhaul in 2014 and a major Crimes 2.0 update in 2023. Modern Torn has real-time features, mobile apps, and a rich API.\\\n\\\nFor Skara Brae, we build for **2026+ expectations**:\\\n- Instant real-time feedback (WebSocket everywhere)\\\n- Mobile-first (PWA, not just responsive)\\\n- API-first design (community tools from day one)\\\n- Type-safe full stack\\\n- Idle/offline progression (server-side tick engine)\\\n\\\n---\\\n\\\n## Multi-Repo Structure\\\n\\\nThe project is split into three repositories. See [Project Plan](joplin://088737f31c514f1da21cc42c0ab6acc1) for full rationale.\\\n\\\n```\\\ngithub.com/{org}/\\\n├── skara-brae-server/          # Rust backend (opened in RustRover)\\\n├── skara-brae-client/          # Vue 3 frontend (opened in WebStorm)\\\n└── skara-brae-common/          # Shared game data, API contract, constants\\\n```\\\n\\\n### Local Checkout Layout (IDE Wiring)\\\n\\\nThe two consumer repos live in different JetBrains IDEs (RustRover, WebStorm). Keep all three as **siblings under one parent folder** — the parent is a plain folder, **not** a git repo:\\\n\\\n```\\\n~/dev/skara-brae/                  # workspace root (plain folder, NOT a git repo)\\\n├── skara-brae-server/             # RustRover opens THIS\\\n├── skara-brae-client/             # WebStorm opens THIS\\\n└── skara-brae-common/             # shared — 3rd window, or \\\"Attach\\\" in either IDE\\\n```\\\n\\\n- **RustRover (server)** — path dependency in `skara-brae-server/Cargo.toml`:\\\n  ```toml\\\n  sb-common = { path = \\\"../skara-brae-common/rust\\\" }\\\n  ```\\\n  Sibling path deps resolve fine. To edit common from inside RustRover: **Settings → Project Structure → Attach** (or add `../skara-brae-common` as a content root).\\\n- **WebStorm (client)** — local file dependency in `skara-brae-client/package.json`:\\\n  ```json\\\n  \\\"@skara-brae/common\\\": \\\"file:../skara-brae-common/ts\\\"\\\n  ```\\\n  plus a Vite alias (`@common` → that path) so HMR picks up data edits. Attach the module likewise to edit it in-window.\\\n\\\n### Shared-Repo Consumption Strategy (Bilingual Package)\\\n\\\n`skara-brae-common` is a **bilingual package** whose source of truth is **language-neutral data + schema**, consumed at build time by both projects via codegen (never hand-duplicated types):\\\n\\\n```\\\nskara-brae-common/\\\n├── data/                  # language-neutral source of truth\\\n│   ├── items/ adventures/ crit-tables/ spells/ recipes/\\\n│   ├── medals/ dungeons/ tower/ expeditions/        (*.json / *.toml)\\\n│   └── schema/            # JSON Schema (entities) + openapi.yaml (API contract)\\\n├── rust/                  # thin crate the server depends on\\\n│   ├── Cargo.toml         #   name = \\\"sb-common\\\"; includes data via include_str! + serde\\\n│   └── src/lib.rs\\\n├── ts/                    # thin package the client depends on\\\n│   ├── package.json       #   name = \\\"@skara-brae/common\\\"; generated types + data re-exports\\\n│   └── src/index.ts\\\n└── codegen/               # data/ + schema  →  rust/ and ts/ bindings\\\n```\\\n\\\n**How sharing actually works:**\\\n- **Static game data** (`data/*.json`) is the single source of truth. Both sides consume at build time: Rust via `include_str!` + `serde`; Vue via Vite JSON import.\\\n- **API contract — server is authoritative.** Expose OpenAPI via [`utoipa`](https://docs.rs/utoipa); generate the client's TS types with `openapi-typescript` (or `orval`). So `api-types/` becomes a *generated* artifact, not hand-written in two languages.\\\n- **Entity types** — define once as JSON Schema in `data/schema/`, generate both Rust structs (`typify` / `schemars`) and TS interfaces (`json-schema-to-typescript`) from `codegen/`. Commit generated output (or generate in CI).\\\n\\\n**Two clarifications:**\\\n1. Do **not** confuse `skara-brae-common` (cross-language, this third repo) with the `sb-shared` crate in the server workspace below — `sb-shared` is a **server-internal** Rust crate (types/errors/constants) and stays inside `skara-brae-server/crates/`. Only truly cross-language artifacts go in the third repo.\\\n2. **Alternative for self-contained CI**: add `skara-brae-common` as a **git submodule** inside both (`server/common/`, `client/common/`) so `git clone --recurse-submodules` yields a complete tree. Cleaner CI, more day-to-day friction (detached HEAD, two-step updates). For solo work, start with the **sibling + path** layout above; switch to submodules only if contributors/CI demand it.\\\n\\\n**Synergy:** Because all content lives as structured data, we can **auto-generate a community game wiki** from `skara-brae-common` (Idle Fantasy does exactly this) — client tooltips and docs from one source.\\\n\\\n---\\\n\\\n## Server Project Structure\\\n\\\n```\\\nskara-brae-server/\\\n├── Cargo.toml                  # Workspace root\\\n├── crates/\\\n│   ├── sb-api/                 # Axum HTTP API server\\\n│   │   ├── src/\\\n│   │   │   ├── main.rs\\\n│   │   │   ├── config.rs\\\n│   │   │   ├── routes/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── auth.rs\\\n│   │   │   │   ├── character.rs\\\n│   │   │   │   ├── adventure.rs\\\n│   │   │   │   ├── combat.rs\\\n│   │   │   │   ├── market.rs\\\n│   │   │   │   ├── guild.rs\\\n│   │   │   │   ├── property.rs\\\n│   │   │   │   ├── education.rs\\\n│   │   │   │   ├── crafting.rs\\\n│   │   │   │   ├── pets.rs\\\n│   │   │   │   ├── blessings.rs\\\n│   │   │   │   ├── inn.rs\\\n│   │   │   │   └── slayer.rs\\\n│   │   │   ├── middleware/\\\n│   │   │   │   ├── auth.rs\\\n│   │   │   │   └── rate_limit.rs\\\n│   │   │   └── ws/\\\n│   │   │       ├── handler.rs\\\n│   │   │       └── channels.rs\\\n│   │   └── Cargo.toml\\\n│   │\\\n│   ├── sb-core/                # Game engine core\\\n│   │   ├── src/\\\n│   │   │   ├── lib.rs\\\n│   │   │   ├── character/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── stats.rs         # BIGINT stat system, tiered display\\\n│   │   │   │   ├── skills.rs\\\n│   │   │   │   └── races.rs\\\n│   │   │   ├── combat/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── engine.rs\\\n│   │   │   │   ├── actions.rs\\\n│   │   │   │   ├── formulas.rs\\\n│   │   │   │   └── crit_tables.rs   # RoleMaster-inspired open-ended d100\\\n│   │   │   ├── adventure/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── templates.rs\\\n│   │   │   │   ├── rewards.rs\\\n│   │   │   │   └── offline.rs       # Offline session reward calculation\\\n│   │   │   ├── economy/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── market.rs\\\n│   │   │   │   └── bank.rs\\\n│   │   │   ├── guild/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── wars.rs\\\n│   │   │   │   └── ranks.rs          # 10-rank progression system\\\n│   │   │   ├── crafting/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── professions.rs    # 12 professions (5 gathering + 7 crafting)\\\n│   │   │   │   ├── recipes.rs\\\n│   │   │   │   └── quality.rs        # Quality roll system (Common→Legendary)\\\n│   │   │   ├── pets/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── breeding.rs\\\n│   │   │   │   └── bonuses.rs\\\n│   │   │   ├── blessings/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   └── effects.rs\\\n│   │   │   ├── workers/\\\n│   │   │   │   ├── mod.rs             # Inn workers (NPC automation)\\\n│   │   │   │   └── tasks.rs\\\n│   │   │   ├── slayer/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   └── tasks.rs\\\n│   │   │   ├── world/\\\n│   │   │   │   ├── mod.rs\\\n│   │   │   │   ├── time.rs         # Day/night cycle\\\n│   │   │   │   ├── weather.rs\\\n│   │   │   │   └── events.rs       # Dynamic world events\\\n│   │   │   └── rules/\\\n│   │   │       ├── mod.rs\\\n│   │   │       └── formulas.rs      # All game formulas in one place\\\n│   │   └── Cargo.toml\\\n│   │\\\n│   ├── sb-db/                  # Database layer\\\n│   │   ├── src/\\\n│   │   │   ├── lib.rs\\\n│   │   │   ├── models/\\\n│   │   │   │   ├── user.rs\\\n│   │   │   │   ├── character.rs\\\n│   │   │   │   ├── item.rs\\\n│   │   │   │   ├── guild.rs\\\n│   │   │   │   ├── property.rs\\\n│   │   │   │   ├── pet.rs\\\n│   │   │   │   ├── crafting.rs\\\n│   │   │   │   └── blessing.rs\\\n│   │   │   ├── queries/\\\n│   │   │   └── migrations/\\\n│   │   └── Cargo.toml\\\n│   │\\\n│   ├── sb-auth/                # Authentication & authorization\\\n│   │   ├── src/\\\n│   │   │   ├── lib.rs\\\n│   │   │   ├── jwt.rs\\\n│   │   │   ├── hash.rs           # argon2 password hashing\\\n│   │   │   └── session.rs\\\n│   │   └── Cargo.toml\\\n│   │\\\n│   └── sb-shared/              # Server-internal shared types, errors, constants\\\n│       ├── src/\\\n│       │   ├── lib.rs\\\n│       │   ├── types.rs\\\n│       │   ├── errors.rs\\\n│       │   └── constants.rs\\\n│       └── Cargo.toml\\\n│\\\n├── migrations/                 # SQL migrations\\\n│   ├── 001_initial.sql\\\n│   ├── 002_character.sql\\\n│   ├── 003_guilds.sql\\\n│   ├── 004_pets.sql\\\n│   ├── 005_crafting.sql\\\n│   └── 006_blessings.sql\\\n│\\\n├── docker-compose.yml\\\n├── Dockerfile\\\n└── README.md\\\n```\\\n\\\n> Note: `sb-shared` above is the **server-internal** Rust crate. The **cross-language** shared repo is `skara-brae-common` (consumed via `sb-common = { path = \\\"../skara-brae-common/rust\\\" }`). See [Multi-Repo Structure](#multi-repo-structure) above.\\\n\\\n---\\\n\\\n## Key Crate Dependencies\\\n\\\n### sb-api (Axum server)\\\n```toml\\\n[dependencies]\\\naxum = { version = \\\"0.8\\\", features = [\\\"ws\\\", \\\"macros\\\"] }\\\ntokio = { version = \\\"1\\\", features = [\\\"full\\\"] }\\\ntower = \\\"0.5\\\"\\\ntower-http = { version = \\\"0.6\\\", features = [\\\"cors\\\", \\\"trace\\\", \\\"compression-gzip\\\"] }\\\nserde = { version = \\\"1\\\", features = [\\\"derive\\\"] }\\\nserde_json = \\\"1\\\"\\\nsb-core = { path = \\\"../sb-core\\\" }\\\nsb-db = { path = \\\"../sb-db\\\" }\\\nsb-auth = { path = \\\"../sb-auth\\\" }\\\nsb-shared = { path = \\\"../sb-shared\\\" }\\\nsb-common = { path = \\\"../skara-brae-common/rust\\\" }   # cross-language game data + types\\\nutoipa = { version = \\\"5\\\", features = [\\\"axum_extras\\\"] } # OpenAPI for client codegen\\\ntracing = \\\"0.1\\\"\\\ntracing-subscriber = \\\"0.3\\\"\\\n```\\\n\\\n### sb-core (Game engine)\\\n```toml\\\n[dependencies]\\\nserde = { version = \\\"1\\\", features = [\\\"derive\\\"] }\\\nrand = \\\"0.9\\\"\\\nchrono = { version = \\\"0.4\\\", features = [\\\"serde\\\"] }\\\nthiserror = \\\"2\\\"\\\nsb-shared = { path = \\\"../sb-shared\\\" }\\\nsb-common = { path = \\\"../skara-brae-common/rust\\\" }\\\n```\\\n\\\n### sb-db (Database)\\\n```toml\\\n[dependencies]\\\nsqlx = { version = \\\"0.8\\\", features = [\\\"runtime-tokio\\\", \\\"postgres\\\", \\\"chrono\\\", \\\"uuid\\\", \\\"migrate\\\"] }\\\nsb-shared = { path = \\\"../sb-shared\\\" }\\\nserde = { version = \\\"1\\\", features = [\\\"derive\\\"] }\\\n```\\\n\\\n### sb-auth (Auth)\\\n```toml\\\n[dependencies]\\\njsonwebtoken = \\\"9\\\"\\\nargon2 = \\\"0.5\\\"\\\nserde = { version = \\\"1\\\", features = [\\\"derive\\\"] }\\\nchrono = { version = \\\"0.4\\\", features = [\\\"serde\\\"] }\\\nsb-shared = { path = \\\"../sb-shared\\\" }\\\n```\\\n\\\n---\\\n\\\n## Database Schema (Core Tables)\\\n\\\n```sql\\\n-- Users\\\nCREATE TABLE users (\\\n    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    username    VARCHAR(32) UNIQUE NOT NULL,\\\n    email       VARCHAR(255) UNIQUE NOT NULL,\\\n    password    VARCHAR(255) NOT NULL,  -- argon2 hash\\\n    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    last_login  TIMESTAMPTZ,\\\n    is_banned   BOOLEAN NOT NULL DEFAULT FALSE,\\\n    is_patron   BOOLEAN NOT NULL DEFAULT FALSE\\\n);\\\n\\\n-- Characters (one per user, or multiple for patrons)\\\nCREATE TABLE characters (\\\n    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    user_id     UUID NOT NULL REFERENCES users(id),\\\n    name        VARCHAR(32) UNIQUE NOT NULL,\\\n    race        VARCHAR(16) NOT NULL,\\\n    class       VARCHAR(16) NOT NULL,\\\n    level       INT NOT NULL DEFAULT 1,\\\n    experience  BIGINT NOT NULL DEFAULT 0,\\\n    gold        BIGINT NOT NULL DEFAULT 100,\\\n    energy      INT NOT NULL DEFAULT 100,\\\n    max_energy  INT NOT NULL DEFAULT 100,\\\n    hp          BIGINT NOT NULL DEFAULT 100,\\\n    max_hp      BIGINT NOT NULL DEFAULT 100,\\\n    mana        BIGINT NOT NULL DEFAULT 50,\\\n    max_mana    BIGINT NOT NULL DEFAULT 50,\\\n    -- Base stats (BIGINT — exponential growth, display tiered)\\\n    strength    BIGINT NOT NULL DEFAULT 10,\\\n    agility     BIGINT NOT NULL DEFAULT 10,\\\n    constitution BIGINT NOT NULL DEFAULT 10,\\\n    intelligence BIGINT NOT NULL DEFAULT 10,\\\n    wisdom      BIGINT NOT NULL DEFAULT 10,\\\n    charisma    BIGINT NOT NULL DEFAULT 10,\\\n    -- Location\\\n    location    VARCHAR(32) NOT NULL DEFAULT 'tavern',\\\n    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()\\\n);\\\n\\\n-- Skills\\\nCREATE TABLE character_skills (\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    skill_name    VARCHAR(32) NOT NULL,\\\n    level         INT NOT NULL DEFAULT 0,\\\n    experience    BIGINT NOT NULL DEFAULT 0,\\\n    PRIMARY KEY (character_id, skill_name)\\\n);\\\n\\\n-- Inventory\\\nCREATE TABLE inventory (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    item_id       UUID NOT NULL,\\\n    quantity      INT NOT NULL DEFAULT 1,\\\n    equipped_slot VARCHAR(16),\\\n    quality       VARCHAR(16) NOT NULL DEFAULT 'common',  -- common, fine, superior, exceptional, masterwork, legendary\\\n    acquired_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()\\\n);\\\n\\\n-- Guilds\\\nCREATE TABLE guilds (\\\n    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    name        VARCHAR(64) UNIQUE NOT NULL,\\\n    description TEXT,\\\n    leader_id   UUID NOT NULL REFERENCES characters(id),\\\n    treasury    BIGINT NOT NULL DEFAULT 0,\\\n    tax_rate    SMALLINT NOT NULL DEFAULT 10 CHECK (tax_rate BETWEEN 0 AND 25),\\\n    level       INT NOT NULL DEFAULT 1,\\\n    reputation  BIGINT NOT NULL DEFAULT 0,\\\n    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()\\\n);\\\n\\\nCREATE TABLE guild_members (\\\n    guild_id     UUID NOT NULL REFERENCES guilds(id),\\\n    character_id UUID NOT NULL REFERENCES characters(id),\\\n    rank         VARCHAR(16) NOT NULL DEFAULT 'initiate',\\\n    reputation   BIGINT NOT NULL DEFAULT 0,\\\n    joined_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    PRIMARY KEY (guild_id, character_id)\\\n);\\\n\\\n-- Properties\\\nCREATE TABLE properties (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    property_type VARCHAR(32) NOT NULL,\\\n    owner_id      UUID REFERENCES characters(id),\\\n    guild_id      UUID REFERENCES guilds(id),\\\n    level         INT NOT NULL DEFAULT 1,\\\n    rent_price    BIGINT,\\\n    sale_price    BIGINT,\\\n    is_listed     BOOLEAN NOT NULL DEFAULT FALSE\\\n);\\\n\\\n-- Market listings\\\nCREATE TABLE market_listings (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    seller_id     UUID NOT NULL REFERENCES characters(id),\\\n    item_id       UUID NOT NULL,\\\n    quantity      INT NOT NULL DEFAULT 1,\\\n    price         BIGINT NOT NULL,\\\n    listed_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    expires_at    TIMESTAMPTZ NOT NULL\\\n);\\\n\\\n-- Adventures log\\\nCREATE TABLE adventure_log (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    adventure_id  VARCHAR(64) NOT NULL,\\\n    result        VARCHAR(16) NOT NULL,\\\n    rewards_gold  BIGINT NOT NULL DEFAULT 0,\\\n    rewards_xp    BIGINT NOT NULL DEFAULT 0,\\\n    started_at    TIMESTAMPTZ NOT NULL,\\\n    completed_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()\\\n);\\\n\\\n-- Education enrollment\\\nCREATE TABLE education_enrollments (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    course_id     VARCHAR(64) NOT NULL,\\\n    enrolled_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    completes_at  TIMESTAMPTZ NOT NULL,\\\n    completed     BOOLEAN NOT NULL DEFAULT FALSE\\\n);\\\n\\\n-- Pets\\\nCREATE TABLE pets (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    pet_type      VARCHAR(32) NOT NULL,\\\n    name          VARCHAR(32),\\\n    rarity        VARCHAR(16) NOT NULL DEFAULT 'common',\\\n    level         INT NOT NULL DEFAULT 1,\\\n    experience    BIGINT NOT NULL DEFAULT 0,\\\n    bonus_type    VARCHAR(32) NOT NULL,\\\n    bonus_value   DECIMAL(5,2) NOT NULL DEFAULT 1.00,\\\n    is_active     BOOLEAN NOT NULL DEFAULT FALSE,\\\n    age_days      INT NOT NULL DEFAULT 0,\\\n    is_dead       BOOLEAN NOT NULL DEFAULT FALSE,\\\n    acquired_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()\\\n);\\\n\\\nCREATE TABLE pet_equipment (\\\n    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    pet_id     UUID NOT NULL REFERENCES pets(id) ON DELETE CASCADE,\\\n    item_id    UUID NOT NULL,\\\n    slot       VARCHAR(16) NOT NULL  -- collar, armor, trinket\\\n);\\\n\\\n-- Crafting professions\\\nCREATE TABLE character_professions (\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    profession    VARCHAR(32) NOT NULL,\\\n    mastery_level VARCHAR(16) NOT NULL DEFAULT 'novice',\\\n    experience    BIGINT NOT NULL DEFAULT 0,\\\n    PRIMARY KEY (character_id, profession)\\\n);\\\n\\\n-- Crafting recipes (discovered)\\\nCREATE TABLE character_recipes (\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    recipe_id     VARCHAR(64) NOT NULL,\\\n    discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    PRIMARY KEY (character_id, recipe_id)\\\n);\\\n\\\n-- Blessings (active)\\\nCREATE TABLE active_blessings (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    blessing_type VARCHAR(32) NOT NULL,\\\n    effect_value  DECIMAL(5,2) NOT NULL,\\\n    activated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    expires_at    TIMESTAMPTZ NOT NULL\\\n);\\\n\\\n-- Inn workers (hired NPCs)\\\nCREATE TABLE hired_workers (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    worker_type   VARCHAR(32) NOT NULL,  -- mercenary, apprentice, gatherer\\\n    quality       VARCHAR(16) NOT NULL DEFAULT 'novice',\\\n    task          VARCHAR(64),\\\n    hired_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    expires_at    TIMESTAMPTZ NOT NULL,\\\n    cost_per_hour BIGINT NOT NULL\\\n);\\\n\\\n-- Slayer tasks\\\nCREATE TABLE slayer_tasks (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    enemy_type    VARCHAR(64) NOT NULL,\\\n    target_count  INT NOT NULL,\\\n    current_count INT NOT NULL DEFAULT 0,\\\n    assigned_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\\n    completed     BOOLEAN NOT NULL DEFAULT FALSE\\\n);\\\n\\\n-- Offline session state\\\nCREATE TABLE offline_sessions (\\\n    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),\\\n    character_id  UUID NOT NULL REFERENCES characters(id),\\\n    session_type  VARCHAR(32) NOT NULL,  -- adventure, skill_training, gathering\\\n    started_at    TIMESTAMPTZ NOT NULL,\\\n    max_duration  INTERVAL NOT NULL DEFAULT INTERVAL '1 hour',\\\n    claimed       BOOLEAN NOT NULL DEFAULT FALSE\\\n);\\\n```\\\n\\\n---\\\n\\\n## Why Rust for a Game Like This\\\n\\\n1. **Performance**: Torn serves millions of requests. Rust handles this with minimal resources — single server can serve thousands of concurrent WebSocket connections.\\\n\\\n2. **Type safety**: Game rules are complex (combat formulas, skill interactions, economy, crit tables). Rust's type system catches bugs at compile time. The `sb-core` crate becomes a verified game engine.\\\n\\\n3. **Fearless concurrency**: Game tick engine processes timers for thousands of players simultaneously. Offline reward calculation runs alongside active gameplay. Tokio + Rust's ownership model makes this correct by construction.\\\n\\\n4. **No GC pauses**: Critical for real-time combat. Rust provides deterministic performance.\\\n\\\n5. **Workspace architecture**: Clean separation between game logic (`sb-core`), persistence (`sb-db`), API (`sb-api`), and auth (`sb-auth`). Each crate compiles independently — fast iteration.\\\n\\\n6. **Ecosystem**: Axum, SQLx, Tokio, Serde are all mature, production-grade crates in 2026.\\\n\\\n---\\\n\\\n## Why Vue.js (Not React/Svelte)\\\n\\\n- **Composition API** maps well to game UI patterns (reactive stat bars, inventory grids, combat logs)\\\n- **Pinia** stores model game state naturally (character store, market store, combat store)\\\n- **Single-file components** keep template + logic + styles co-located — good for complex game UIs\\\n- User preference (stated)\\\n- **PWA support** via Vite plugin for mobile-like experience without a separate app\\\n\\\n---\\\n\\\n## Related Notes\\\n\\\n- [Game Concept Overview](joplin://fcd381c235694f29abf73665317a40f5) — Full game design with crafting, pets, inn, guilds\\\n- [Combat System & Stat Scaling Design](joplin://5ff1fed180fa4b39b4cdb925f34c1008) — Crit tables, BIGINT stats, combat formula\\\n- [Idle Fantasy Inspirations](joplin://fea2d81f0414484fa4edf126f8ca17ed) — Offline sessions, workers, pets, blessings\\\n- [Lore & World Building](joplin://2c8e2a844da0497e8a93c820ec8901a1) — Districts, NPCs, storyline\\\n- [Project Plan](joplin://088737f31c514f1da21cc42c0ab6acc1) — Roadmap, MVP scope, repo structure\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":20184}]"
metadata_diff: {"new":{"id":"980c3eb587294e4383474b94988f2f88","parent_id":"d1892c7c531848f5a5a3ac5e1749f7cf","latitude":"0.00000000","longitude":"0.00000000","altitude":"0.0000","author":"","source_url":"","is_todo":0,"todo_due":0,"todo_completed":0,"source":"joplin-desktop","source_application":"net.cozic.joplin-desktop","application_data":"","order":1781184768037,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":0},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-09-09T16:22:52.590Z
created_time: 2026-07-31T18:49:42.206Z
is_locked: 0
type_: 13