/lip/v1. The Admin API, SQLite layout, snapshots, and user interface are non-normative implementation choices.
Architecture
packages/referenceowns deterministic loyalty behavior and exports a versioned state snapshot.packages/storagedefines the small state-store contract without choosing a database.packages/storage-sqlitestores one current snapshot per key using SQLite, WAL mode, and an upsert transaction.packages/serverexposes the normative HTTP binding and a separate, authenticated Admin API.apps/adminrenders operational state without importing the engine or reading SQLite directly.
Start and persistence
http://127.0.0.1:3210, seeds synthetic restaurant activity on first use, and writes .lip/reference.db. Every successful mutation saves the new engine snapshot before the HTTP response is returned.
The snapshot includes its format version and a fingerprint of the configured program. Startup rejects a snapshot created for an incompatible program instead of silently applying the wrong earning or tier policy.
Useful controls:
--reset is intentionally explicit. A normal restart hydrates members, balances, point lots, reservations, ledger entries, adjustments, and idempotency records from the existing database.
Admin boundary
Openhttp://127.0.0.1:3210/admin/ and sign in with the configured Admin/API key. The server exchanges it for an eight-hour, HttpOnly, SameSite=Strict session cookie. Admin data is served from /admin/api/v1/snapshot; protocol clients do not need or use this route.
The Configure view supports versioned points-program drafts, validation, optimistic publish, retained revision history, and rollback. Compatible changes to earn rates, tiers, expiration, eligibility, and rewards take effect live without replacing member balances or immutable ledger history. Program ids and currencies cannot change after publication. Writes use a double-submit CSRF token tied to the local Admin session; Bearer-authenticated automation is also supported.
The same view reads program-model capability metadata from the Admin snapshot, so operators can compare points, visits, wallet credit, paid membership, and hybrid structures. All five models are runnable in the reference engine. Hybrid programs accrue each configured unit independently, expose every member balance, and reserve rewards against the reward’s configured cost unit.
The Admin supports:
- Program health, issued and outstanding liability, and tier distribution
- Member search, balances, tier progress, and expiration buckets
- Immutable ledger search and operation filtering
- Earning policy, tier ladder, and reward-catalog inspection
- Program draft, validate, publish, discard, and rollback operations
- Local audit records for program writes
- Tenant-scoped users with fixed roles and permissions
- One-time API key issuance, expiration, revocation, and access audit history
- Program model planning for future configuration work
- Protocol, storage, and endpoint diagnostics
The shared development token is suitable for bootstrapping a local reference environment. The runtime also supports persisted tenant-scoped users and hashed API keys with role-based authorization, expiration, revocation, CSRF-protected Admin writes, and audit records. Users and API keys optionally carry
allowed_location_ids, which scopes the location registry and the per-location report to a franchisee’s own locations.Operational guards
The reference HTTP server enables a fixed-window, per-remote-client limit of 120 authenticated protocol requests per minute. CLI flags and container environment variables can change the request count and window. Every limited response carriesRateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset; exhausted clients receive HTTP 429 and Retry-After.
The CLI and container also emit structured JSON request records with timestamp, request id, method, normalized path, response status, duration, and response size. API keys, authorization headers, and bodies are deliberately excluded. Applications embedding createReferenceServer can provide a custom requestLogger or disable rate limiting explicitly.
An authenticated GET /metrics endpoint exports low-cardinality request counters and duration sums/counts in Prometheus text format. Unknown URLs and Admin assets are normalized to bounded labels to avoid cardinality growth.
Maintenance and write-freeze
The reference server can refuse/lip/v1 writes while keeping reads and /health available, for planned maintenance windows or coordinated migrations.
Start frozen from the CLI or container:
--write-freeze and LIP_WRITE_FREEZE=true (or 1) are equivalent; either is enough to start frozen.
Once running, an authenticated operator can toggle the flag at runtime through the Admin API:
GET returns the current {"write_frozen": boolean} status. The POST requires an authenticated Admin/API key and, for session-cookie callers, a valid CSRF token, matching every other Admin write.
While frozen, any /lip/v1 request classified as a protocol write (including evaluate) receives 503 application/problem+json with {"code": "write_frozen"} and a Retry-After header. Reads stay available, and GET /health reports the current state as write_frozen alongside the existing status fields, so monitoring can detect maintenance windows without calling the Admin API.
Segments and reward campaigns
Campaign authoring is a non-normative platform feature under/admin/api/v1; it does not add campaign concepts to /lip/v1. Operators can persist static member segments, target a catalog reward, run the campaign manually, and inspect durable run summaries in Admin.
Platform API clients can also define dynamic segments over member status, tier, available balance, and exact-match attributes. Campaigns with starts_at are picked up by the embedded scheduler; ends_at prevents late issuance.
Reward cards can be added, edited, or removed as validated program-draft changes before they are published. Publishing rejects removal of a reward that still backs an active issued reward or a saved campaign.
The runner creates deterministic campaign_id:member_id issued reward ids, so repeated runs skip members already targeted. Customer BFFs consume the result through the portable issued-reward list and redemption operations.
Paid membership
Membership plans are program configuration. The non-normative Admin API grants or ends a member entitlement after an external billing system reports payment state. Active plans can multiply earning and gate rewards throughreward.metadata.membership_plan_ids; expired memberships are lapsed by the embedded scheduler. Billing and customer authentication remain outside LIP.
Engagement integrations
The Admin API calculates member, balance, daily ledger, reward, and campaign aggregates from engine state. CRM member exports are available as JSON or formula-safe CSV and filter out members without marketing consent by default. Persisted messaging jobs target existing static or dynamic segments. Marketing deliveries enforcemember.attributes.marketing_consent; transactional deliveries are explicit. The bundled webhook adapter signs each message, retains delivery attempts and errors, and retries with bounded exponential backoff. Provider SDKs plug in through MessagingConnectorAdapter without changing protocol routes.
Extension path
SQLite adapters implement the synchronousStateStore<T> contract. PostgreSQL uses AsyncStateStore<T>, normalized engine repositories, optimistic revisions, transaction advisory locks, and scheduler leases. POS and ordering integrations should continue to target /lip/v1; vendor-specific mappings belong in adapter packages and conformance fixtures.