The CLI is the product, and the way you get it was nowhere on the site. You
had to already know the URL of a script served out of public/.
Two placements, one command:
- The homepage hero gets the loud version, directly under the lede and above
the fold -- a bordered dark panel, the command at full size, Copy alongside.
- Every page carries a compact version in the rail, between the brand and the
nav. Present on arrival, never competing with navigation.
Both come from renderInstallCommand() in one module. The homepage builds its
HTML as a string and the rest of the site is JSX, which is precisely the shape
that lets one copy of a command drift while the other stays right -- so there
is one definition and SiteShell renders it rather than restating it.
The command keeps its flags: `curl -fsSL`. Without -f, curl prints an HTTP
error body and still exits 0, so a 404 gets piped into sh; without -L the
install breaks the first time the URL redirects. This is the form install.sh
already documents in its own header.
Copy is one delegated listener on document for any [data-copy] button, mounted
site-wide in the layout. Delegation because the two placements arrive by
different rendering paths and a document listener does not care which; it also
means the next copy button needs the attribute and no wiring. It falls back to
a throwaway textarea + execCommand outside a secure context, where
navigator.clipboard is simply undefined, so the button never no-ops silently.
The contract tests pin the command, both placements, and that the clipboard
payload equals the visible text -- a Copy button that hands over something
other than what is on screen is worse than no button. They also read
public/install.sh and assert it is #!/bin/sh and documents this exact command,
so `| sh` cannot quietly become a lie.
apps/logicsrc-web: 13 new tests pass, 46 total. The ontology-api contract file
fails to resolve @logicsrc/validators, which it also does on a pristine
origin/master -- unbuilt workspace package, unrelated to this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The .btn skin is built for the light page ground (white fill, ink text).
The topnav rail is #101418, so those buttons landed there as stray
light-mode chips -- and ".bar a{color:var(--rail-text)}" outranks ".btn"
on color (0,1,1 vs 0,1,0), so <a class="btn">Settings</a> painted
rail-text on a white fill: 1.08:1, effectively invisible.
Scope a rail variant to .bar: transparent fill, rail-text label, and a
border at 38% rail-text (3.40:1, clearing the 3:1 non-text minimum --
--rail-line is only 1.4:1 and vanishes). Sign in keeps the green accent,
the focus ring moves green -> mint (3.60:1 -> 9.03:1), and .faint/.dim
in the bar resolve to --rail-dim.
Settings and Sign out go 1.08:1 and light-chip-on-dark to 17.20:1.
Body buttons are untouched -- every rule is .bar-scoped.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The "Connect the CLI" card handed out:
LOGICSRC_API=https://app.logicsrc.com logicsrc login
logicsrc teams push <team> prod --env .env
logicsrc teams pull <team> prod --env .env
Two things are wrong with that, and both survived a release.
Since #109 addressed vaults as <team> <project> <env>, push and pull take
three positionals. The hint passes two, so pasting it exits with a missing-
argument error -- the card is not merely stale, it is broken.
The LOGICSRC_API prefix sets the variable to the value the CLI already
defaults to (DEFAULT_API_URL, #107), so on the hosted app it does nothing
while reading like a required step. It is now emitted only when the origin
is not the default, which is the case it exists for: self-hosting.
`--env .env` is dropped for the same reason -- it restates the option's own
default, and sitting next to the new <env> positional it made one flag and
one argument look like the same thing.
Same stale two-argument form fixed in the post-install hint (install.sh) and
the accept-invite message, and in the empty-vault-list prompt on the card.
CLI_HINT moves to src/lib/cli-hint.mjs so a test can assert on the rendered
commands without standing up express and the database, matching how the
other lib-level views are covered. The tests pin the argument count rather
than the prose: restyling the card stays free, dropping an argument does not.
apps/pwa: 13/13 pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two bugs on the dashboard, both fixed by handing appBar/CLI_HINT the request.
Sign-out was broken for everyone. csrfGuard rejects any POST whose _csrf does
not match the mc_csrf cookie, and /auth/logout is a POST that is not on the
exempt list, but the sign-out form carried no hidden field -- every click
answered 403 "bad csrf token". appBar now takes the request rather than the
user, because it needs the token as well as the identity. The field is written
out instead of reusing csrfInput(): html.mjs is the view layer and imports
nothing, and pulling in session.mjs would drag the database driver with it.
The "Connect the CLI" snippet still printed $PUBLIC_ORIGIN, so users on
app.logicsrc.com were told to point LOGICSRC_API at the generated Railway
hostname. #105 added requestOrigin() for exactly this and fixed the device-flow
URLs; the dashboard hint was missed. It now follows the request too, which is
not a hardcode swap -- the same deployment answering on its Railway hostname
still self-describes correctly.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`logicsrc login` defaults to https://logicsrc.com (#104), but every path it
needs returns 404 there: the apex runs the marketing app, while /cli/* lives in
apps/pwa on its own service.
Proxy those paths from the app that owns the apex, the same way CommandBoard is
already proxied. No DNS record, no Railway custom domain, and no subdomain --
and it makes the CLI's existing default origin correct rather than requiring
another change to chase it.
Pointing the apex at the pwa instead was the obvious alternative and is wrong:
the pwa serves `/` too, so it would take the marketing site down with it.
Proxied:
/cli/:path* the device-code and loopback login flows
/api/me identity
/api/credshare/:path* the credential-sharing API used after login
/auth/:path* /cli/authorize and /cli/device are behind requireAuth,
so an unauthenticated visitor is redirected here; without
it the browser half of the flow dead-ends on a 404
Order matters and is asserted: CommandBoard owns a catch-all /api/:path*, so
/api/me and /api/credshare/* have to match first or CLI auth silently goes to
the wrong service.
Rewrite construction is factored into pure functions so the ordering is testable
without booting Next, and degrades cleanly: with CREDENTIALS_APP_URL unset the
output is byte-identical to what shipped before.
Requires CREDENTIALS_APP_URL on the logicsrc-web service, pointing at the
credentials app's origin.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`logicsrc login --device` told users to open
https://logicsrc-credentials-production.up.railway.app/cli/device even when they
had reached the app on the real domain. /cli/device/code built verification_uri
from `config.origin`, which is a single fixed value read from $PUBLIC_ORIGIN, so
the response was wrong for every hostname except the one that variable happened
to name.
Derive the origin from the request instead: whatever host the CLI called is the
host it gets sent back to. Express honours X-Forwarded-Proto/Host here because
server.mjs sets `trust proxy` behind Railway's TLS terminator.
Deliberately scoped to the two device-flow URLs. The WebAuthn expectedOrigin in
passkey.mjs stays pinned to config.origin — validating a signature against a
host the caller supplied would defeat the check.
Note this fixes which URL is *printed*; the host still has to route to this
service for the link to load.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`update` was three hardcoded console.log lines: it printed 0.1.0 as both
current and latest, claimed "already up to date", and never checked or
installed anything. `--version` was hardcoded the same way.
A version comparison alone could not have worked either. install.sh ships
a tarball of the master branch, not a tagged release, and
packages/cli/package.json has been 0.1.0 since the repo began, so version
equality says "up to date" no matter how far master has moved. The commit
is the real signal.
- install.sh records ref/commit/version/installed_at to
$LOGICSRC_HOME/install.json. The sha comes from GitHub's
Accept: application/vnd.github.sha media type, so this needs no jq.
It is resolved before the download on purpose: if master moves
mid-install we under-report (a spurious update) rather than falsely
claim to be current.
- update compares the installed commit against the remote ref head,
falls back to version comparison for installs predating the manifest,
and reports why it reached its verdict instead of just asserting one.
--check reports without installing; otherwise it re-runs the installer.
- --version now reads the package's real version.
Verified against live GitHub in all three states: matching commit, stale
commit, and no manifest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`logicsrc login` defaulted to http://localhost:4010 — a dev origin that
doesn't exist on an installed machine, so the printed authorize URL went
nowhere. It now defaults to the hosted credentials app (apps/pwa), reads
the documented $LOGICSRC_API, and only reuses a stored apiUrl once that
identity has actually completed a login (which is how machines got stuck
pointing at localhost). Note logicsrc.com is the marketing site and has
no /cli routes.
The loopback flow is also unusable over SSH: redirect_uri is
http://127.0.0.1:<port>/callback, which resolves to the *browser's*
machine, not the CLI's. Added a device-authorization flow — the CLI
prints a short user_code, the human approves it from any browser:
POST /cli/device/code mint device_code + user_code (10 min TTL)
GET /cli/device approve page (login required; typo-tolerant)
POST /cli/device approve/deny (CSRF-guarded browser form)
POST /cli/device/token CLI polls -> lsk_ API key
device_code is stored sha256-hashed, single-use, with authorization_pending
/ slow_down / access_denied / expired_token poll semantics. The CLI picks
the flow automatically (SSH/CI/no-DISPLAY -> device), with --device/--web
to force it and a fallback to loopback against servers without /cli/device.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(web): move Hire Us pricing to $400/hour metered billing (PRD 0002)
Replaces the $250/week retainer with a $400/hour rate billed against actual
hours, invoiced through CoinPay after the client approves them. A 10-hour
minimum engagement replaces the week as the unit of commitment.
The weekly price lived in 12 places, not the 3 the PRD listed: the front-page
Hire Us section, the Top-Level Pages list, /hire-us metadata, /pricing
(metadata, two FAQ answers, rate bullet), /about, llms.txt, skill.md, and the
Hire Us form success message.
Metered billing rather than a committed weekly block, because the old
"recurring CoinPay invoice" copy documented a mechanic that never existed:
/api/payments/create makes a single one-shot payment, not a subscription.
- coinpay-checkout derives amount_usd from hours x 400 instead of a hardcoded
250, validates hours as quarter-hour increments at or above the minimum, and
returns 422 before calling CoinPay on bad input. Payment metadata carries
billing/hours/rate_usd_per_hour in place of interval.
- project-request returns a rate, billing mode, and minimum; no amount exists
until hours are approved.
- CoinPay config block documents COINPAY_RATE_USD_PER_HOUR / COINPAY_BILLING /
COINPAY_MINIMUM_HOURS instead of a weekly amount and interval.
- New real /terms route replacing the SPA stub: what is billable, the
approve-then-invoice flow, the minimum, cancellation on one week's notice,
and an explicit clause that existing engagements keep their terms until both
sides agree in writing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(mcp): advance prd_next_id expectation to 0003 for PRD 0002
The standards test asserts prd_next_id against the live prd/ directory, so
adding prd/0002-hourly-hire-us-rate.md moves the next free id to 0003. This
assertion advances with every PRD added to the repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything the two shipped PRD phases deferred, minus what is called out below.
Storage (Phase 2)
@logicsrc/openontology gains a SQLite/Turso adapter. It hydrates the read
model at open, serves reads synchronously — a query evaluator that awaits per
triple pattern is unusable — and buffers mutations as SQL that flush() writes
in one transaction. Versioned idempotent migrations; indexes over subject,
predicate, entity-valued object, status, both time axes, aliases, and external
ids; FTS5 for label/alias search. The append-only status log is replayed on
open, so retractions, supersessions, and merge redirects survive a reopen.
REST + SSE + OpenAPI (Phase 2)
16 paths under /api/ontologies in logicsrc-web, described at
/api/ontologies/openapi and referencing the published JSON Schemas rather
than restating them. No token is read-only; a curator token can apply; an
agent token can propose and cannot apply. Idempotency-Key on mutations,
revision ETags, 409 on a stale base revision, and an SSE stream that emits
the same event objects as the JSON endpoint.
MCP (Phase 2)
OpenOntology and OpenPRD surfaces on the standards server: spec/manifest/
schema/queries and PRD spec/index as resources, 11 ontology tools and 6 PRD
tools, 7 prompts. Read-only by default; OPENONTOLOGY_MCP_WRITABLE=1 buys
proposals, never applies — the denial is the shared policy layer, not a
second rule that could drift.
Interoperability (Phase 3)
RDF/Turtle export and import of the reified profile, plus the plain triple
for asserted relationships so a consumer wanting only the accepted graph gets
one. SHACL for 5 of 7 constraint kinds; `unique` and `query` are reported as
unmapped in both the return value and the generated Turtle, because a shape
that quietly means something narrower is worse than no shape.
Source adapters (Phase 3)
CSV, JSON, YAML, NDJSON, Markdown, generic JSON HTTP, and GitHub. All produce
PROPOSED change-set operations with source, evidence selector, run id, and
confidence attached; fetch is injected so ingestion is offline and testable.
Each declares its capabilities, so "nothing was deleted upstream" is never
confused with "this adapter cannot see deletions" — none of the seven can.
TUI + explorer
Keyboard-first panels (types, entities, claims, sources, queries, change
sets, validation, audit) as plain strings that survive SSH and 60 columns;
status is a glyph and a word, never colour alone; the key bar wraps rather
than truncating. Wired as `logicsrc ontology tui`. A read-only web explorer
at /openontology/explore with entity and claim views showing status, both
clocks, confidence, sources, evidence, and append-only history — plus an
/openprd page for the companion standard.
Bugs found and fixed while testing
- the API built a new engine per request, so `explain` could never find a
resultId from a prior request; engines are now cached per role
- the TUI status bar called engine.validateOntologyPackage(), appending a
package.validated event on every repaint; it now uses the pure validator
Verification: 76 new tests (527 total across the monorepo, all passing); full
build green; the libSQL adapter is exercised against real files, the API
through its route handlers, and MCP over an in-memory transport.
Not included: PWA review/approval write flows (they need an auth story this
deployment does not have), OWL/RDFS mappings, SPARQL/Cypher/Datalog query
adapters, and Phase 4 governed actions. The compatibility matrix marks those
"planned", not "supported".
Refs: prd/0001-add-logicsrc-openontology-spec.md
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Mount @logicsrc/plugin-agentmail in the CommandBoard API so members can
list/read/search/send mail over the existing agentbbs Mailu server.
- src/agentmail.ts: inject imapflow + nodemailer + mailparser drivers into
the plugin's Mailu transport (kept out of the plugin by design); env-driven
builder (AGENTMAIL_BACKEND=mailu → bbs Mailu, else in-memory singleton for
dev/test). Handles STARTTLS cert-vs-loopback via AGENTMAIL_SMTP_TLS_SERVERNAME.
- index.ts: register agentMailPlugin + 7 routes under /api/plugins/agentmail/*
(mailboxes, list, read, search, send, PATCH flags, delete). Acting member
from x-agentmail-member header (else AGENTMAIL_MEMBER), paid-gated;
MailAccessError->402, DraftError->422.
- 6 route tests (in-memory backend) + contract test plugin-id list updated.
commandboard-api 20/20, builds clean.
Note: run `npm install` to refresh the lockfile for the 3 new deps.
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>