Feature Specification: Optional standalone runtime
Feature Branch: rare-moleCreated: 2026-10-13 Status: Implemented and locally verified Input: Optionally run camply with Python and TypeScript, served entirely by Python, using SQLite without PostgreSQL, Prometheus, Celery, or Redis/Valkey. Complete Spec-Kit planning, then implement following the explicit implementation request and requirements-review approval.
User Scenarios & Testing
User Story 1 - Start the application without services (Priority: P1)
An owner runs the complete application on a personal computer or small server, using local storage and one website/API address.
Why this priority: A usable deployment must not require separate database, worker, broker, or monitoring services.
Independent Test: Start a prepared installation with backing services absent, open the website and a saved deep link, save user/scan state, restart, and verify that state remains.
Acceptance Scenarios:
- Given built assets and fresh writable storage, when standalone starts, then storage is initialized/upgraded and the website and API are available at one origin without excluded services.
- Given an installed application, when a user refreshes a dashboard/admin deep link, then the website loads; missing API routes and assets produce genuine errors.
- Given missing assets, unwritable storage, an unsupported database, or conflicting worker settings, when startup is attempted, then it fails with a corrective message before serving.
- Given saved users, scans, preferences, results, and pending alerts, when the app restarts or storage is backed up/restored, then those records remain usable without the source checkout or frontend build tools.
User Story 2 - Discover and monitor campsites (Priority: P1)
An owner populates catalogs, creates scans, and receives matching alerts without separate worker processes.
Why this priority: The new deployment must perform the campsite finder's core work.
Independent Test: Use synthetic providers and notification transports to refresh catalogs, create two subscriptions sharing a target, observe one check and individually filtered alerts, then exercise interruption and recovery.
Acceptance Scenarios:
- Given an empty catalog, when an administrator requests sync, then the request returns promptly, progress/failures are visible, and metadata becomes searchable without availability selections. Daily refresh also runs.
- Given two eligible subscriptions sharing one target, when a check is due, then one provider check serves both and alerts match individual filters.
- Given paused, suspended, deleted, or edited subscriptions, when work executes or alerts send, then current ownership, eligibility, target association, dates, filters, and preferences control delivery.
- Given provider failure, when work retries, then previous results remain intact, checks do not overlap, and other targets/HTTP requests proceed.
- Given a committed detection, when the process stops before delivery, then the pending alert survives restart and retries within its limit. A crash after external acceptance may cause a duplicate. Closing and reopening is a new detection.
User Story 3 - Operate either deployment mode (Priority: P2)
Administrators understand runtime health and manage users/scans without intentionally absent monitoring appearing broken.
Why this priority: Local health must be honest and the existing distributed deployment must remain supported.
Independent Test: Exercise administration/status/catalog sync in both modes; verify disabled features are distinct from actual failures.
Acceptance Scenarios:
- Given standalone, when operations opens, then scheduler health, discovery, running work, sync status, and recent outcomes appear; broker panels and historical charts are hidden with an explanation.
- Given local recent history, when records exceed 1,000 or 24 hours, then old records are removed; the UI explains that history clears on restart while pending alert state survives.
- Given disabled trends, when a client requests them directly, then it receives intentional unavailability without an external query. A failed runner instead reports unhealthy status.
- Given distributed mode, when current workflows run, then workers, broker inspection, metrics, trends, separate frontend hosting, and admin controls remain supported.
Edge Cases
- Slow catalogs/provider requests do not block HTTP or alert delivery. Repeated sync requests share an active job; one failed provider does not stop others.
- Deleted, relinked, expired, or newly nonmatching scans are skipped before delivery.
- Transient failures retry finitely; malformed payloads/missing credentials produce terminal outcomes.
- A failed result transaction creates neither partial results nor delivery intents. Accepted external delivery followed by a crash can duplicate an alert.
- Multiple subscriptions share checks; multiple app instances sharing one database are unsupported.
- Unknown work values remain unknown; disabled features are not reported as outages.
- Static routes and operational history cannot expose credentials or database/repository files.
Requirements
Functional Requirements
- FR-001: Owners MUST select standalone/distributed independently of auth/environment; distributed remains default.
- FR-002: Standalone MUST serve built frontend/API at one origin, support deep links, and retain genuine asset/API errors.
- FR-003: File-backed SQLite MUST support fresh initialization, populated upgrades, persistence, and backup/restore of all saved state and pending alerts.
- FR-004: Runtimes MUST share discovery/provider/comparison/filter/eligibility/notification behavior rather than duplicate the scanner.
- FR-005: Standalone MUST provide bounded, supervised polling, daily sync, delivery, finite retry/backoff, and graceful shutdown without overlapping target checks.
- FR-006: Standalone MUST commit notification intents atomically with results, recover pending delivery, document at-least-once semantics, and allow a new reopening alert.
- FR-007: Standalone MUST install/start without Celery, Redis, Prometheus client, or PostgreSQL driver libraries and without contacting those services.
- FR-008: Both runtimes MUST retain auth, CSRF, invitations, authorization, ownership, preferences, and admin controls; standalone pending alerts MUST recheck current eligibility/filters.
- FR-009: Administration MUST expose capabilities and local status/history with finite retention, distinguishing unsupported, unknown, empty, and failed states.
- FR-010: Documented Task workflows MUST install/build/launch/verify standalone; prepared artifacts MUST need only Python at runtime.
- FR-011: Initial/manual catalog sync MUST work without availability selections, coalesce active refresh requests, and report progress/per-provider failures.
- FR-012: Startup MUST reject unsupported database/worker settings and missing/unwritable resources with corrective messages.
- FR-013: Background calls/parsing and database contention MUST NOT prevent unrelated HTTP responses.
- FR-014: Static serving/history MUST protect credentials and files outside frontend assets, retaining existing security boundaries.
- FR-015: Automated behavior, migration, restart, minimal-install, browser, and distributed regression checks MUST verify the feature.
Key Entities
- Runtime capabilities: Execution mode, monitoring support, scheduler health, and last discovery.
- Shared target/subscription: Existing de-duplicated provider/date request and individually filtered user interest.
- Pending notification: Detection event, subscription, credential-free alert content, retry timing/count, and terminal delivery/skip result.
- Job attempt: Bounded recent job/event identity, provider/target context, timing, and outcome.
Success Criteria
Measurable Outcomes
- SC-001: A prepared installation completes initial sync → search → create → detect → notify with one website/API origin and zero backing services or excluded libraries.
- SC-002: Fresh/populated upgrades and restart/backup/restore preserve all seeded state and resume pending alerts.
- SC-003: Two eligible subscribers produce one provider check per due cycle and alerts only for currently matching subscriptions.
- SC-004: In a deterministic small-install test with successful external responses and no backlog, delivery begins within 60 seconds of detection; unrelated health responds within 2 seconds while provider/catalog work is held pending.
- SC-005: Fault tests cover failure before/after persistence, restart, retry exhaustion, and changed eligibility; no committed pending alert is silently lost and failed checks preserve results.
- SC-006: Local admin explains limits and reports injected runner failure as unhealthy; distributed regression/live monitoring smoke checks pass.
- SC-007: An installed artifact works outside the checkout without build tools, including deep links, login, and mutations; missing paths and traversal have correct errors.
- SC-008: Formatting, lint, types, tests, API contracts, and applicable pre-commit/browser checks pass before implementation is complete.
Assumptions
- Personal/small shared deployments use local disk and one Python process per database. Multiple replicas, network filesystems, and cross-database conversion are out of scope.
- Node/pnpm build TypeScript before deployment; browsers run compiled JavaScript. Existing separate hosting remains supported.
- Existing
none,session, andauth0modes remain independent. Providers, alerts, and optional Auth0 need internet access. - Historical Prometheus charts are omitted locally; a replacement time-series system, generic durable queue, and new alert channels are out of scope.
- Local attempt history retains at most 1,000 records for 24 hours and clears on restart. Terminal notification records retain 24 hours; pending retries survive.
- Tests use synthetic providers/transports and no live credentials.
Clarifications
Session 2026-10-13
No critical ambiguity requires a new question. Scope follows the earlier draft, the explicit implementation request, and the approved requirements-review recommendations. Build-time tools, process limits, monitoring omissions, and delivery guarantees are explicit assumptions; storage/interface mechanics are resolved in design. No user answer or approval is invented.