Skip to content

Release 5dee6a3cb67f

Source: 5dee6a3cb67f255da4f243aa426d000444145fd6

Documentation fingerprint: b7dcd317ee5725563301fe0d477dde2eb10d0aeea73fc0e5fef687789e22456b

5dee6a3 Harden collection, recovery, dependency health and versioned documentation
00442dd Enforce local radius, discovery filters, and product variant boundaries
f7ad08c Build PriceDip research, tracking, owner access, and release infrastructure

The following guides are preserved for this exact application release.

Sign in with Google, create a watchlist, paste an HTTPS listing URL, choose its condition, and set a USD item-price target. PriceDip verifies the listing and seller before recording observations. Watchlists can be public or private. Account-specific ZIP code and radius remain private.

The initial release supports 25 active products per account. Edit the target or pause monitoring from product details. Prices are checked approximately hourly; provider blocks or retries can delay observations. History begins with the first verified observation.

Search a product name or model. Local discovery requires your own ZIP code and radius (1–500 miles). Direct search links remain available when search engines block automatic retrieval. Open a tracked product and request Qwen research; the brief is queued and cites retrieved sources. Reload the product after processing.

An available, eligible fixed-price USD offer below the target generates an event, including on the initial check. One event is generated until the price rises back to the target or above. Changing a target rearms eligibility. Email delivery is retried using an idempotency key. Shipping and taxes are not part of the target; shipping is shown when known. Auctions, membership pricing, and unverified offers are excluded.

Visitors can browse published products and events. They cannot search externally, generate research, change targets, or read location/account settings. The sample showcase never sends alerts.

Discovery filters support retailer, condition, and maximum item price. Local tracking requires configured account location and rejects listings outside the selected radius. Paused products can be reactivated only within the active-product limit.

Discovery accepts product names, model numbers, and exact supported HTTPS listing URLs. Filter by retailer, condition, seller name, maximum item price, and delivery or pickup. Unknown delivery methods do not pass a delivery filter. Local searches use your private ZIP and radius from Settings; unverifiable locations are excluded. Shipping is shown separately when the source provides it.

Direct sales by Amazon, Walmart, Best Buy, Target, and verified manufacturer stores qualify when the source explicitly identifies the seller. A retailer domain alone is not sufficient evidence.

eBay requires at least 99% positive feedback and a feedback score of 100. Other marketplace sellers require attributable ratings of at least 4.5/5 from 20 reviews, or a business identity independently verified against an established retailer.

Missing seller identity or reputation fails closed. Qwen explains evidence but cannot override deterministic eligibility checks. A verified marker describes recorded evidence, not a guarantee.

Local marketplace listings also need a verifiable location within the account’s configured radius. Listings behind login or without reputation/location evidence may produce no qualifying results.

Every recorded observation includes source evidence and timestamp. A new extraction rechecks eligibility before an alert. Different conditions and listing identities are not merged. Changed product titles halt automatic observation until the owner adds the new listing.

Ambiguous structured data containing multiple product variants is rejected. Marketplace ratings must use a five-point scale; unrecognized scales do not qualify.

The manufacturer-store registry includes Sony Electronics, Apple, Dyson, and Samsung. These official storefront domains are explicitly classified as direct sellers; arbitrary new domains are not admitted.

React/Vite renders the dark amber interface. Fastify serves the frontend and API on loopback port 4350. A separate Node worker performs hourly collection, Qwen requests, and email delivery. Private SearXNG listens on 4352. Ollama remains the existing local service; PriceDip selects qwen2.5:3b by default.

SQLite uses WAL, foreign keys, busy timeout, versioned migrations, and indexes. Accounts own watchlists. Products reference watchlists. Observations and research reference products. Jobs have durable leases; events and the email outbox are transactionally created with price observations. Worker restart reclaims expired leases.

Observe acts as a narrow Google sign-in broker. PriceDip issues browser-bound state. Observe accepts only the fixed PriceDip destination and exact owner email, signs a 60-second audience-restricted assertion, and PriceDip consumes state once before issuing its own HttpOnly Secure session cookie. Each application retains independent sessions. Observe being unavailable does not stop monitoring or existing PriceDip sessions, but new sign-ins require it.

Source adapters normalize observations. Seller policy, price parsing, variant consistency, and alert crossings run in deterministic code. Qwen receives bounded untrusted text and produces schema-validated research with source IDs. Invalid citations fail the job.

HTTPS only, no URL credentials, public-address validation, pinned DNS for HTTP extraction, bounded redirects and response sizes. Browser extraction is isolated to the original origin with pinned resolution and bounded time. Private source URLs are rejected.

Verified observations and events persist. Completed job records expire after seven days; active jobs and failures persist until resolved. Chart responses are bounded without deleting stored observations. Secrets are external runtime configuration.

Browser extraction blocks all WebSocket connections as well as requests outside the original origin. Product dialogs trap keyboard focus and restore it when closed. The System status link opens the dedicated PriceDip page in Observe.

Collection jobs take priority over research jobs. Research is cached for 24 hours. Both fresh source extraction and local ZIP/radius eligibility are rechecked during hourly monitoring. Exact listing identifiers, model, color, size, and condition are kept stable; ambiguous offer collections are rejected. Seller evidence must be fresh within 15 minutes when recording an alert. Public chart samples are selected in SQLite, while original observations remain intact. The overview supports bounded limit/offset pagination.

Use Node 22.22 or newer. Run npm ci, npm test, and npm run build in the app repository. Set DB_PATH to an isolated development database. Start the compiled API with npm start and frontend with npm run dev. The Vite proxy forwards API/auth calls to loopback port 4350. Never use production cookies in tests.

Tests cover seller boundaries, SSRF exclusions, strict price comparisons, observations, deduplicated alerts, and lease recovery. Browser verification covers public views, research, responsive layouts, and owner controls using isolated fixtures.

Documentation is canonical in pricedip-wiki. Update affected guides for each behavioral/configuration/dependency change, then run node scripts/docs.mjs record ../pricedip-wiki from the tracked application checkout. This records a reviewed source fingerprint; never use it to conceal stale explanations.

The check mode rejects a different source tree. Release mode generates API/configuration references and a commit-linked release page only after the contract passes. PRs declare documentation impact and include the companion wiki change. Untrusted PR jobs have no deployment secrets.

Pull requests must declare exactly one documentation impact and link a companion pricedip-wiki commit or pull request. The required validation check enforces this declaration, source fingerprints, tests, build, and a high-severity production dependency audit. Direct dependencies and Action revisions are pinned; Dependabot proposals must pass the same documentation gate.

GitHub Actions validates tests, TypeScript, production build, and the exact documentation contract. The release job resolves the wiki revision, generates references, pushes wiki publication, waits for the published release manifest, then deploys an immutable application artifact through the existing Cloudflare SSH route.

The production host runs pricedip.service, pricedip-worker.service, and pricedip-search.service. Persistent data is stored under /var/lib/pricedip. Runtime secrets live in /etc/pricedip/runtime.env. The application release lives beneath /opt/pricedip/releases and current is a symlink.

A server-side lock serializes deployment. Activation runs only after an HTTP health check; failed activation restores the previous symlink and services. Database migrations must remain compatible with the previous release; destructive changes require a separate migration/recovery plan.

The Cloudflare tunnel publishes only the web service. Search, Ollama, metrics, and SQLite are not exposed publicly. Changes to shared host infrastructure must use its existing repository and wiki workflow.

Dependabot opens update PRs. Lockfiles and pinned Actions support repeatable builds. Run behavior and deployment tests before merging. Production secrets must never enter pull_request jobs.

The Starlight wiki runs on GitHub Pages. The app source commit is available in the wiki release manifest and app health. A failed or mismatched wiki publication blocks application deployment.

Each release page preserves a complete snapshot of the reviewed guides and generated API/configuration references. The application Documentation link opens its exact release. The commit ledger covers the complete repository history. Deployment and rollback records retain both application and wiki commit IDs in the private database.

Observe registers PriceDip and PriceDip Wiki as separate applications. The app navigation links to Observe; the portal links to both sites and repositories.

The existing minute collector fetches private PriceDip Prometheus metrics and forwards them through Alloy/Grafana. Dedicated dashboards cover availability, requests, errors, memory, worker heartbeat, queue age, stale products, collection outcomes, Qwen duration/errors, email delivery, backup age, restore evidence, and docs alignment.

Telemetry warns at three minutes and fails at five. Tracked products with previous successful observations become stale after two hours. A blocked provider is a data-quality limitation; it does not mean the web service is down. Missing observations and missing metrics do not count as healthy.

Public views contain aggregate operational evidence. Logs, account settings, ZIP codes, query text, private delivery details, and secrets are excluded. Existing owner access controls diagnostics. Operational incidents use Observe’s existing mechanisms; product-price emails are independent.

PriceDip does not depend on Observe for collection, research, or existing sessions. New Google sign-ins require the broker. Documentation synchronization failure blocks releases and remains visible through release evidence.

Qwen model availability and SearXNG health are checked every minute. These checks describe dependency reachability; retailer accessibility is measured by individual collection outcomes. Never infer verified offer coverage from a healthy search service. Source counters use only fixed retailer IDs, with no product names, URLs, or location labels.

The backup timer creates a consistent SQLite snapshot daily and records a timestamp only after a successful integrity check. It restores the snapshot into a separate verification database and validates integrity and required tables; this does not modify the active database. Backups remain in the protected host backup directory and are included in existing host backup coverage.

Before activation, create a fresh snapshot. To roll back application code, restore the previous current symlink and restart the web/worker services. Keep schema changes additive so this remains safe.

For data restoration, stop the worker and web service, preserve the current database and WAL files, verify the desired backup in isolation, install it under the service account, then start services and run health checks. Restore never runs automatically over current data.

The recovery test records evidence separately from backup creation. Observe distinguishes fresh backups from tested recovery. Preserve snapshots before upgrades and keep at least 14 daily backups.

Use an isolated test database for target crossings and email outbox checks. Stop/restart only PriceDip’s worker to verify heartbeat degradation and lease recovery. Simulate provider and Qwen errors using fixtures. Do not disrupt shared Ollama or Observe to test PriceDip.

Run the current release’s scripts/rollback.sh as root to activate the prior immutable release. This records the transition without reverting backward-compatible database additions. Run it again to return to the newer release after verification. Daily snapshots are opened as independent restored databases, integrity-checked, and queried before restoration evidence is recorded.

The service uses no paid API or AI subscriptions. Automatic source access is best effort; hourly scheduling cannot guarantee that a retailer will permit retrieval.

Amazon, Walmart, Best Buy, and Target require explicit seller identity and a single exact offer. Marketplace merchant attribution may not be present in structured data. eBay API requires configured production credentials and any required approval; without it, public extraction may not expose reputation evidence. Craigslist frequently lacks attributable reputation. Facebook Marketplace and OfferUp can require login. These situations are excluded, not silently trusted.

Local radius is enforced only when the source supplies verifiable location data. ZIP centroids provide approximate distance, not driving distance. Missing location is not assumed nearby.

Qwen is a local research aid. Inaccessible reviews or specifications are reported as missing; AI text is never used as observed price data. Search-engine snippets may be stale; offers are extracted from their source.

Email is bounded by free service allowances. Failed deliveries remain queued. Alerts compare item prices before shipping and tax.

Sample products, research, charts, and events are explicitly illustrative. They never populate real watchlists or monitoring metrics.

PriceDip reserves a conservative email budget of 30 successful deliveries per day and 500 per rolling 31 days by default. The Resend account is shared: provider quota rejection leaves messages pending with backoff. The sender uses the existing verified nutsnews.com domain; this does not change the PriceDip website domain.

Generated from application commit 5dee6a3cb67f255da4f243aa426d000444145fd6.

MethodRouteAccess
GET/healthzPublic / authentication flow
GET/api/sessionPublic / authentication flow
GET/auth/loginPublic / authentication flow
GET/auth/callbackPublic / authentication flow
POST/api/owner/logoutOwner session + same-origin writes
GET/api/public/overviewPublic / authentication flow
GET/api/public/products/:idPublic / authentication flow
GET/api/owner/settingsOwner session + same-origin writes
PUT/api/owner/settingsOwner session + same-origin writes
POST/api/owner/watchlistsOwner session + same-origin writes
GET/api/owner/productsOwner session + same-origin writes
POST/api/owner/searchOwner session + same-origin writes
POST/api/owner/productsOwner session + same-origin writes
PATCH/api/owner/products/:idOwner session + same-origin writes
POST/api/owner/products/:id/:actionOwner session + same-origin writes
GET/internal/metricsPrivate bearer token
GET/internal/readyzPrivate bearer token
VariableDefault or requirement
APP_ORIGINhttps://pricedip.ramideltoro.com
PORT4350
DB_PATH/var/lib/pricedip/pricedip.sqlite
AUTH_BRIDGE_SECRETSecret / optional; configure privately
METRICS_TOKENSecret / optional; configure privately
SEARXNG_URLhttp://127.0.0.1:4352
OLLAMA_URLhttp://127.0.0.1:11434
QWEN_MODELqwen2.5:3b
BROWSER_EXTRACTIONtrue
PLAYWRIGHT_BROWSERS_PATH/opt/pricedip/browsers
RESEND_API_KEYSecret / optional; configure privately
EMAIL_FROMSecret / optional; configure privately
EBAY_CLIENT_IDSecret / optional; configure privately
EBAY_CLIENT_SECRETSecret / optional; configure privately
EMAIL_DAILY_LIMIT30
EMAIL_MONTHLY_LIMIT500