OpenPRD has existed as a document (docs/openprd.md), a front-matter schema, a
template, and this repo's prd/ collection. Nothing enforced it. This adds the
reference implementation.
@logicsrc/openprd
- parser: front-matter + the eight `##` sections + numbered requirements.
`###` stays content so a long Requirements section can be organized, and
headings or R#-shaped lines inside code fences are ignored
- validation splits the standard's four conformance rules (filename,
front-matter schema, id-matches-prefix, eight sections in order) from
lint (empty section, missing priority tag, numbering gaps, duplicate R#,
date order, one-sided supersession, stale index). Conformance failures are
errors; --strict promotes the rest. Stable codes, file, line, hint
- collection rules the per-file view cannot see: unique ids, monotonic
numbering with no gaps, 0000 reserved for the template, cross-references
that resolve
- lifecycle enforced rather than advisory: Draft cannot jump to Final,
terminal statuses do not resume, Superseded must name its replacement
- deterministic index generation, so `prd index` is idempotent and CI can
diff it
- front-matter rewriting that leaves the body byte-identical
- the optional LogicSRC task bridge the standard describes: each R# becomes
one logicsrc.task, validated against logicsrc-task.schema.json before it
is emitted; creator DID derived from the author email
CLI: logicsrc prd init|new|list|show|validate|lint|index|status|next|tasks|
export. Exit codes stable for CI (0 ok, 1 invalid, 2 usage, 3 not found).
Conformance bundle: packages/schemas/fixtures/openprd/ — 6 documents that must
validate and 12 that must fail, each naming the error code it must produce.
Several rules depend on the filename, so every fixture records the name it is
validated as.
Docs: an Implementation section in docs/openprd.md (CLI, validation model,
task bridge, conformance bundle), the spec added to the site's docs surface,
nav and sitemap entries, and a README section.
Verification: 76 new tests; full monorepo build and all 451 workspace tests
pass. The suite dogfoods this repo — prd/ validates with zero errors and zero
warnings, the embedded template is byte-identical to docs/openprd/0000-
template.md, and all 210 requirements in PRD 0001 map to schema-valid tasks.
prd/README.md is regenerated by the tool it now ships.
Refs: docs/openprd.md
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements OpenPRD 0001 through Phase 0 (specification, schemas, example,
docs surface) and Phase 1 (local engine, CLI, conformance tests).
Schemas (17 contracts, JSON Schema Draft 2020-12, additionalProperties:false)
manifest, namespace, entity-type, property, relationship-type, constraint,
query, action, entity, claim, source, evidence, changeset, review, approval,
event, package — registered in @logicsrc/validators and exported from
@logicsrc/schemas under https://logicsrc.com/schemas/openontology/.
@logicsrc/openontology
- canonical JSON + sha256 package digests; YAML, JSON, NDJSON, and inline
authoring all compile to the same bytes, so digests are authoring-agnostic
- id profile: compact / IRI / urn with one canonicalization rule, prefix
bound by a Namespace object so IRIs reverse unambiguously
- validation: schema, graph (domain/range, datatypes, dangling refs),
provenance (source-or-firstParty, agent runId, derivation inputs), policy
(excerpt limits, licensing, visibility, staleness) and declared
constraints; four severities, stable codes, text/json/yaml/markdown
- portable triple-pattern query AST: multi-hop, 14 operators, asOf and
recordedAsOf, per-status filtering, distinct/order/limit, explanation
mode, and enforced depth/binding/row limits
- append-only store: claims are immutable; dispute/retract/supersede append
status transitions and the effective status is the latest one
- change sets: 9 operations, atomic pre-flight, conflict detection on stale
base revisions, semantic diff with duplicate-identity warnings and
affected-query deltas, per-operation reviewer decisions
- policy: agents propose but can never apply — the denial keys on actor
type, so every scope plus high confidence plus --yolo still cannot apply;
merges need approval, bulk retractions need two, undeclared action side
effects are denied
- JSON-LD 1.1 export/import with PROV-O aliases and lossy-field reporting
- pluggable signature envelope with a jws-ed25519 reference profile and a
fail-closed trust policy
CLI: logicsrc ontology init|validate|lint|build|inspect, entity, claim, query,
changeset, import, export, audit. Reads take --format, writes default to a
proposal, exit codes are stable for CI.
Example: examples/openontology/ethereum-ecosystem — 12 entity types, 17
relationship types, 63 entities, 169 claims, 25 sources, 31 evidence records,
5 saved queries, every claim lifecycle state, and a pending merge proposal.
All data is fictional; the directory is removable without affecting any core
test.
Docs: docs/openontology{,-governance,-interoperability}.md, a real
/openontology route, homepage + nav + sitemap entries, and a root README
section.
Verification: 112 new tests; full monorepo build and every workspace test
pass; conformance bundle (18 valid + 13 invalid fixtures) runs against the
published schemas alone; Node.js 25 and Bun 1.3 produce byte-identical
digests, revisions, event trails, and query results.
Not included (later PRD phases): MCP resources, REST/SSE, Turso adapter, TUI
and PWA surfaces, RDF/SHACL mappings, source adapters, governed actions.
Refs: prd/0001-add-logicsrc-openontology-spec.md
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds apps/pwa: an Express + libSQL/Turso app that is now the home of team
credential sharing, with the moshcode-style auth stack ported and reskinned to
match logicsrc.com (light theme, Inter, green accent).
apps/pwa
- auth: email/password (scrypt), passkeys (WebAuthn), CoinPay OAuth, cookie
sessions, and lsk_ API keys for the CLI via a loopback OAuth-PKCE flow
(/cli/authorize + /cli/token). Ported from the moshcode PWA.
- credshare API (/api/credshare/*): teams, members, invites, vaults, sealed
grants, ciphertext secrets, audit — authed by session OR Bearer lsk_ key.
Zero-knowledge: only ciphertext + sealed vault keys + public keys stored.
- teams dashboard, accept-invite, and settings (API keys) pages, server-rendered
in the LogicSRC brand (lib/html.mjs).
- migrations (libSQL) 001_auth + 002_credshare, migrate-on-boot; Turso via
TURSO_DATABASE_URL / TURSO_AUTH_TOKEN, or a local file db for dev.
- trimmed moshcode-specific approvals/credits/push/deliver.
CLI
- `logicsrc login` now does browser loopback OAuth-PKCE against the app and
stores an lsk_ token (email-OTP removed); --token for CI. Client repointed.
Distribution
- install.sh (served at logicsrc.com/install.sh) installs the CLI from the
GitHub repo: tarball -> npm install -> `npm run build:cli` -> logicsrc wrapper.
- root build:cli builds only the CLI's workspace chain (skips web/api/next).
Cleanup
- removed the commandboard-api credshare backend (superseded by the PWA) and its
Supabase/Turso stores + libsql dep; commandboard-api tests green (40).
- removed the Next.js /teams page (the PWA is the web UI now).
Verified end-to-end: two accounts register on the PWA, mint lsk_ keys, CLI login
uploads identity keys, owner pushes an encrypted .env, teammate invited ->
accepted -> granted -> pulls the exact file. Server stores ciphertext only.
Full workspace build + tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a `team` credential provider + team/member management so teammates can
share secrets by email instead of passing .env files over chat. Fully E2E:
the server only ever stores ciphertext, per-member sealed vault keys, and
public keys — it never sees a plaintext value or the vault DEK.
Plugin (@logicsrc/plugin-credential-sharing)
- crypto.ts: X25519 identity keys, per-vault DEK (secretbox), DEK sealed to
each member's pubkey (crypto_box_seal), value encrypt/decrypt (libsodium)
- identity.ts: local ~/.logicsrc/identity.json (0600) holding the device key
+ API token; never uploads the secret key
- client.ts: typed /api/credshare client
- providers/team.ts: `team:<slug>/<vault>` CredentialProvider (inspect,
readValues=decrypt, write=encrypt, rollback); fingerprints match env so
env<->team diffs line up
- fixes latent libsodium-wrappers ESM load bug (createRequire) here + in
github-secrets
Server (commandboard-api /api/credshare)
- zero-knowledge router: email-code auth, keys, teams, members, invites,
vaults, sealed grants, ciphertext secrets, audit; membership authz in app
- CredShareStore abstraction: in-memory (dev/tests) + Supabase (prod)
- Resend email transport for login codes + invites (no-op -> echoes locally)
- supabase migration: credshare_* tables, deny-by-default RLS
CLI
- real `logicsrc login` (email code -> token + key upload)
- `logicsrc teams create/list/invite/accept/members/vaults/grant/push/pull`
Web (logicsrc.com/teams + /teams/accept)
- management surface only (browser holds no private key, never decrypts):
login, view teams/members/vaults, invite, accept
Tests: crypto round-trip, server contract (invite->accept->push->grant->pull
+ authz boundaries), and a real HTTP+client+crypto E2E asserting the server
never holds plaintext. Full workspace build + tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reusable AdUnit component (div[data-cp-ad] + ad.js via next/script) placed
in-content on the blog index and post pages, scoped to /blog/* only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The page band flipped from coming-soon to available, replacing the old
`logicsrc credentials plan --from env --to railway` example. Point the e2e
check at the stable `logicsrc credentials providers` line instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New @logicsrc/plugin-credential-sharing: a provider-neutral secret-sync engine
with env/.env, Doppler, Railway, and GitHub Secrets adapters behind one
CredentialProvider contract.
- engine: inspect -> diff -> plan -> approve -> sync -> rollback -> audit/export
- dry-run is the default for sync; --approve writes; destructive changes gated
- fingerprint-based diffs (salted SHA-256); raw values never printed or stored in
plans/runs/audit; rollback pre-image kept in a 0600 .logicsrc vault (gitignored)
- github-secrets is write-only for values (sealed-box via libsodium), so it cannot
be a sync source or value-restoring rollback target
- CLI: real `logicsrc credentials <providers|inspect|diff|plan|approve|sync|
rollback|audit|export>` (replaces the prior stub)
- 4 JSON schemas registered in @logicsrc/validators
- flip logicsrc.com/credential-sharing band from coming-soon to available
- 37 tests pass; full env->env lifecycle verified; artifacts schema-validate
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dynamic /blog/rss.xml returned 500 in CI (no Supabase env) and the E2E
still asserted the old static feed's hand-written items.
- /blog, /blog/[slug], and /blog/rss.xml now degrade gracefully (empty feed/
list, HTTP 200) when Supabase is unavailable, instead of throwing.
- E2E: assert the always-present channel <title>LogicSRC Blog</title> and a
looser xml content-type, dropping the removed static post titles.
Verified with `next dev` and no Supabase env (CI conditions): rss/blog/sitemap
all return 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
More AEO audit fixes (the content-independent quick wins):
- Generated 1200x630 OpenGraph/Twitter card (app/opengraph-image.tsx);
drop the SVG fallback and use summary_large_image.
- /pricing page with question-style headings and FAQPage JSON-LD; clarifies
the spec/tooling is free and implementation is $250/week.
- /llms-full.txt — full markdown of the curated docs concatenated for
large-context RAG ingestion.
- GitHub link added to both navs (SPA rail + SiteShell) and Pricing nav item;
/pricing added to the sitemap.
Verified in a running build: og image renders as PNG and is referenced in
head; /pricing serves FAQ + schema; /llms-full.txt concatenates docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses the top cross-engine AEO finding — every route previously served
the homepage SPA. /about and /docs are now distinct routes with their own
server-rendered content and titles.
- /about: substantive about page (what LogicSRC is, the standards surface,
CommandBoard.run reference impl, GitHub, hire-us) — derived from public
positioning, no fabricated team.
- /docs + /docs/[slug]: render the repo's docs/*.md (curated public set) via
marked, statically generated at build (no runtime fs dependency).
- Drop about/docs from the catch-all; add doc URLs to the sitemap.
Verified in a running build: /about and /docs serve unique content with
distinct titles; /docs/[slug] renders each markdown doc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only the .rail sidebar is dark; the .workspace content area is on the
light (#f6f7f4) page background. The previous blog styling assumed a dark
workspace, so text was light-grey on white (unreadable) and the link
green was too light.
- Darken the global link color to #0a7d59 (readable on white); content
links only — rail nav stays inherited.
- Repaint .blog-content (post HTML) for a light surface: dark body text,
light code/pre, light borders.
- Blog index/post: dark titles, readable grey meta, light row borders;
light-themed footer.
- Add post thumbnails to the /blog index from featured_image.url.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add SiteShell (rail nav + workspace + footer) and wrap /blog and
/blog/[slug] in it so they share the site's dark chrome instead of
rendering as bare standalone pages.
- Style rendered post HTML (.blog-content) for the dark workspace.
- Links were `color: inherit` everywhere, so content-area links matched
body text and were invisible. Give links a distinct accent (#5ac8a6);
keep the rail nav and buttons on their own colors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an autoblog webhook receiver and a Supabase-backed blog to logicsrc-web
(the app had no Supabase usage before).
- Migration: blog_posts table (RLS: public reads published, service-role
writes). Applied to the linked project.
- POST /api/webhooks/blog: verifies the Standard Webhooks signature against
BLOG_WEBHOOK_SECRET via @profullstack/autoblog verifyAndParse (no admin
user — shared secret only) and upserts the post by slug.
- /blog index + /blog/[slug] render published posts from the table.
- /blog/rss.xml and /sitemap.xml are now dynamic, generated from the table;
removed the static public/sitemap.xml and public/blog/rss.xml.
- BLOG_WEBHOOK_SECRET added to .env.example.
Verified end-to-end: a signed sample post delivered 200 and appeared in the
index, post page, RSS, and sitemap; build + typecheck pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the Vite single-page app + custom Node server.js with a Next.js
16.2.6 App Router app.
- proxy.ts (src/proxy.ts): www.logicsrc.com -> logicsrc.com 301 over https,
preserving path + query (the original request, now via Next 16 Proxy).
- One SSR page via an optional catch-all ([[...slug]]) that renders the same
marketing/spec page for each known top-level route (/docs, /blog, /openspec,
...) and 404s unknown paths, preserving existing canonical URLs. Markup is a
faithful server-rendered port of the old main.ts (SEO upgrade over the prior
client render); interactivity (hire-us form, CoinPay button, section scroll)
moves to a client component.
- API routes ported to app/api/**: hire-us coinpay-checkout + project-request,
oauth/coinpay start/callback/session, webhooks/coinpay. Shared logic in
src/lib/coinpay.ts (eligibility, payment-rail selection, webhook verify,
HMAC session sign/verify, cookies).
- commandboard-api (/health + /api/boards|tasks|plugins/*) is no longer mounted
in-process; next.config.ts proxies those paths to COMMANDBOARD_API_URL via
afterFiles rewrites (our own /api routes match first).
- Build/start switch to next build / next start. Contract tests rewritten to
exercise proxy.ts, the route handlers, and pure helpers directly (21 passing);
Playwright webServer updated.
Deployment (Railway): set COMMANDBOARD_API_URL to the commandboard-api service
URL and run it as its own service; root start now runs next start.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Checks /api/oauth/coinpay/session on load and updates the Connect button
to reflect the authenticated user's email when already connected.
Also cleans the coinpay_oauth query param from the URL after OAuth callback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>