# Phase 1: Foundation and Shared State - Research

**Researched:** 2026-02-27
**Domain:** Rust desktop workspace foundations, secure token persistence on Windows, SQLite shared-state service design
**Confidence:** MEDIUM

<user_constraints>
## User Constraints (from CONTEXT.md)

### Locked Decisions
- Credentials are configured manually via config file (no in-app setup UI in Phase 1).
- Credential types: GitHub OAuth credentials and Shopify admin token.
- Validation is format-only during entry/save; live API verification happens later during sync flows.
- Clients connect over localhost HTTP API.
- Service auto-starts when the first client launches.
- Client behavior on service issues: retry for a bounded window, then enter degraded mode with stale reads.
- Write conflict policy uses optimistic concurrency with version checks.
- Canonical recipient identity is an internal UUID mapped to external IDs.
- Relationship shape: recipient has many packages; package has many items.
- Partial ingest is allowed with nullable fields.
- Missing required data must be explicitly surfaced as unresolved/missing-field indicators for follow-up.
- Ownership mapping uses mixed mode: Shopify-linked mappings with manual override support.
- Phase 1 runtime expectation is packaged app workflow (not raw multi-process dev launch as default path).
- Windows 10 and Windows 11 are both supported on a best-effort basis.
- Packaging baseline artifact is an unsandboxed ZIP distribution.
- Logging includes both console output and rotating file logs.

### Claude's Discretion
- Secret storage implementation: use Windows Credential Manager when available, with encrypted local fallback when unavailable.
- Exact retry window/backoff tuning before degraded mode.
- UX shape for how missing required fields are indicated, as long as unresolved data is clearly visible.
- Rotation policy details (file count/size thresholds) for bounded log growth.

### Deferred Ideas (OUT OF SCOPE)
- None - discussion stayed within phase scope.
</user_constraints>

<phase_requirements>
## Phase Requirements

| ID | Description | Research Support |
|----|-------------|-----------------|
| AUTH-01 | User can configure and securely store a GitHub API token. | Config-file ingestion contract, secret indirection to OS vault, and zero-secret logging policy. |
| AUTH-02 | User can configure and securely store a Shopify API token. | Same secure-secret pipeline as AUTH-01 with provider-specific validation rules. |
| DATA-04 | System can persist normalized recipient/package records in shared state storage. | SQLite schema shape, repository boundaries, concurrency-safe write patterns, and migration-first persistence model. |
| ITEM-02 | Item model supports non-1:1 mapping with Shopify products. | Mixed-mode ownership mapping using explicit link tables plus manual override metadata. |
| DSC-01 | System stores Discord username per recipient. | Recipient schema includes nullable Discord identity fields and unresolved markers. |
| DSC-02 | System stores Discord user ID per recipient. | Same normalized recipient identity model with external identity map table. |
| PLAT-01 | v1 supports Windows only. | Windows-first packaging baseline, Credential Manager integration, and local service bootstrap conventions. |
</phase_requirements>

<research_summary>
## Summary

Phase 1 should establish a strict separation between operator-edited config and secret material. Config remains manual and plaintext-safe (non-secret metadata and references), while secret bytes are resolved via a credential provider abstraction that defaults to Windows Credential Manager and uses encrypted fallback storage only when required. This aligns with locked decisions and keeps auth handling auditable before sync integrations arrive in Phase 2.

For shared state, the stable approach is a single local HTTP service process owning SQLite access. Clients never write the database directly. Instead they submit versioned mutations through repository/use-case endpoints that enforce optimistic concurrency and emit stable API responses for stale-read degradation mode. This avoids lock thrash and keeps conflict handling deterministic.

For normalization, use internal UUID primary keys and explicit external identity mapping tables. Recipient-package-item relationships should be first-class, with a dedicated ownership mapping table that can reference Shopify entities when present while still allowing manual items not tied 1:1 to Shopify products.

**Primary recommendation:** Build a Windows-first Rust workspace with a local HTTP service that exclusively owns SQLite, and enforce secure-secret indirection plus optimistic concurrency from day one.
</research_summary>

<standard_stack>
## Standard Stack

### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| Rust stable toolchain | stable | Primary implementation language for app + service | Matches project constraint and gives strong type safety for data contracts |
| Slint | current stable | Desktop UI runtime for Windows app shell | Matches chosen GUI stack in project constraints |
| sqlx (SQLite) | current stable | Typed SQLite access and migrations | Strong fit for Rust + SQLite shared-state with migration workflow |
| axum + tokio | current stable | Localhost HTTP API for shared service | Lightweight async API stack with predictable composition |
| tracing + tracing-subscriber | current stable | Structured logging and log routing | Enables dual sink output (console + rotating files) |

### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| keyring | current stable | Windows Credential Manager integration | Primary secret backend on Windows |
| ring + aes-gcm (or equivalent) | current stable | Encrypted fallback secret storage | Only if keyring backend unavailable |
| uuid | current stable | Internal UUID identity keys | Canonical identifiers for recipient/package/item |
| serde + serde_json + toml | current stable | Config/schema serialization | Config-file-only credential metadata and API contracts |
| tower | current stable | HTTP middleware (timeouts, retries, request IDs) | Service stability and consistent API behavior |

### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| axum | actix-web | Similar capability; axum generally simpler for composable service skeletons |
| sqlx | rusqlite + hand-written migrations | Lower abstraction but more manual safety burden |
| keyring | custom DPAPI bindings only | More control, but higher implementation and maintenance overhead |
</standard_stack>

<architecture_patterns>
## Architecture Patterns

### Recommended Project Structure
```
crates/
  app/             # Slint desktop shell, bootstrap, client API calls
  core/            # Domain models, IDs, validation, error contracts
  integrations/    # Provider clients and token metadata contracts
  service/         # HTTP API, repositories, migrations, process runtime
```

### Pattern 1: Secret indirection via references
**What:** Config file stores non-secret fields and secret references, never secret bytes.
**When to use:** All GitHub/Shopify credential flows in Phase 1.
**Example:**
```rust
pub struct ProviderCredentialConfig {
    pub provider: String,
    pub client_id: Option<String>,
    pub secret_ref: String,
}
```

### Pattern 2: Repository with optimistic concurrency
**What:** Versioned records in SQLite with compare-and-swap updates.
**When to use:** All mutable recipient/package/item aggregates in shared service.
**Example:**
```rust
UPDATE recipients
SET latest_note = ?, version = version + 1
WHERE recipient_id = ? AND version = ?;
```

### Anti-Patterns to Avoid
- **Config-embedded secrets:** Violates secure handling goals and leaks via backups/logs.
- **Direct SQLite writes from clients:** Breaks concurrency guarantees and shared contract stability.
- **Flat recipient rows for shipping state:** Blocks future relationship growth and mixed item ownership.
</architecture_patterns>

<dont_hand_roll>
## Don't Hand-Roll

| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Secret vault integration | Custom Windows credential APIs from scratch | `keyring` abstraction first | Avoids platform edge-case handling in Phase 1 |
| SQL migration orchestration | Manual SQL execution with ad hoc ordering | `sqlx` migration system | Keeps schema rollout deterministic and reviewable |
| HTTP retry/degrade loops in each handler | Copy-pasted retry code per endpoint | Shared middleware/service-layer retry policies | Centralizes behavior and prevents drift |
| Log file rotation | Custom rollover implementation | battle-tested appender/rolling strategy via tracing ecosystem | Prevents unbounded log growth bugs |
</dont_hand_roll>

<common_pitfalls>
## Common Pitfalls

### Pitfall 1: "Secure" config that still leaks secrets
**What goes wrong:** Secrets accidentally serialized in config or error logs.
**Why it happens:** Shared structs used for both persisted config and runtime credentials.
**How to avoid:** Separate persisted config DTOs from runtime secret-bearing types; redact all token-like fields in log serializers.
**Warning signs:** Token strings appear in panic traces, debug logs, or config snapshots.

### Pitfall 2: SQLite contention under multi-client writes
**What goes wrong:** Busy locks, dropped writes, or inconsistent updates.
**Why it happens:** Multiple processes write directly without unified service ownership.
**How to avoid:** Single writer process pattern (service-owned DB) with optimistic concurrency and bounded retry.
**Warning signs:** Intermittent `database is locked` errors and phantom state overwrites.

### Pitfall 3: Premature over-modeling of external IDs
**What goes wrong:** Schema hard-codes provider assumptions and breaks when data is partial.
**Why it happens:** External IDs used as primary keys and required columns too early.
**How to avoid:** Internal UUID primaries + external identity map tables + nullable enrichment fields.
**Warning signs:** Cannot persist recipient until all upstream systems resolve.
</common_pitfalls>

<code_examples>
## Code Examples

### Secret provider abstraction
```rust
pub trait SecretStore {
    fn get(&self, secret_ref: &str) -> anyhow::Result<String>;
    fn set(&self, secret_ref: &str, value: &str) -> anyhow::Result<()>;
}
```

### Conflict-aware mutation response
```rust
pub enum UpdateOutcome {
    Updated { new_version: i64 },
    Conflict { expected: i64, actual: i64 },
}
```

### Bounded rotating log setup contract
```rust
pub struct LogPolicy {
    pub max_files: u16,
    pub max_file_mb: u16,
    pub mirror_to_console: bool,
}
```
</code_examples>

<sota_updates>
## State of the Art

| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Single binary with in-memory shared state | Dedicated local service + client app split | Common modern desktop ops pattern | Better multi-client behavior and crash isolation |
| Plain config token storage | OS keychain + secret references | Established baseline for desktop apps | Stronger default security posture |
| Flat shipment rows only | Normalized recipient/package/item aggregates | Needed for operational tracking complexity | Supports ownership decoupled from Shopify product mapping |
</sota_updates>

<open_questions>
## Open Questions

1. **OAuth callback shape for GitHub in config-only workflow**
   - What we know: Phase 1 requires GitHub OAuth credentials and manual config entry.
   - What's unclear: Whether callback exchange is in Phase 1 or deferred to Phase 2 integration implementation.
   - Recommendation: Define config schema for OAuth metadata now; implement exchange behavior in integration-phase code path.

2. **Exact stale-read cache source during degraded mode**
   - What we know: Clients retry then degrade to stale reads.
   - What's unclear: Whether stale view comes from service snapshot endpoint, local cache file, or last successful client payload.
   - Recommendation: Standardize on service-owned snapshot endpoint to keep consistency across clients.
</open_questions>

<sources>
## Sources

### Primary (HIGH confidence)
- Project artifacts: `.planning/PROJECT.md`, `.planning/ROADMAP.md`, `.planning/REQUIREMENTS.md`, `.planning/phases/01-foundation-and-shared-state/01-CONTEXT.md`
- GSD planning/research templates and role guidance under `C:/Users/decid/.codex/get-shit-done/`

### Secondary (MEDIUM confidence)
- Rust ecosystem conventions recalled from established usage patterns (sqlx, axum, keyring, tracing)

### Tertiary (LOW confidence)
- None
</sources>

<metadata>
## Metadata

**Confidence breakdown:**
- Standard stack: MEDIUM - architecture-aligned recommendations without live external version verification
- Architecture: HIGH - directly constrained by locked context decisions
- Pitfalls: HIGH - based on common failure modes in local-service + SQLite designs

**Research date:** 2026-02-27
**Valid until:** 2026-03-27
</metadata>

---

*Phase: 01-foundation-and-shared-state*
*Research completed: 2026-02-27*
*Ready for planning: yes*
