AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
A Nuxt 4 layer that wraps your website in a recording instrument — every visit, capture, and error, human or machine, streamed raw to where you decide to double down or kill.
Developer toolsTypeScript · VueGitHub @incubrainOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
IncuBrain Foundry is a Nuxt 4 template optimised for creating AI-native businesses
Taken from the repository README
Loading the full README.
Read the full README ↗A Nuxt 4 layer for standing up a marketing site that streams the full signal of whether it is working.
Foundry is the wireframe. You write content, deploy, and wire nothing — the site captures what visitors do (visits, and intent through the convert components) and what the site itself reports (errors, warnings), buffers it server-side in one envelope, and hands it to an external consumer over an authenticated cursor endpoint. We pull it into Polaris; you can point a Grafana-class puller at the same endpoint. Foundry ends at the stream — no charts, no dashboards, no anomaly detection inside the site.
The second pillar is agent-readiness. Agentic traffic is expected to dwarf human
traffic, so Foundry sites ship MCP tools, llms.txt, raw-markdown redirects, and a strong
SEO/OG stack — and every signal row is classified human | agent | bot server-side, because
that split is itself signal.
Stack: Nuxt 4 · Nuxt Content · Nuxt UI · Tailwind v4 · TypeScript
npm install @incubrain/foundry
// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@incubrain/foundry'],
})
# .env — this is the entire required surface
NUXT_SIGNAL_EXPORT_TOKEN=<random-secret> # bearer token for the export endpoint
NUXT_PUBLIC_SITE_ID=my-site # optional — defaults to the request host
Write content under content/, deploy, point your consumer at
/api/_signals/export. See examples/foundry for a complete app.
Everything the site captures — analytics events and error/warning logs — becomes one
SignalRow in a capped server-side ring buffer. Nothing is pushed anywhere. An external
consumer pulls with a cursor.
client events → signal provider ─┐
client errors → errors.client ───┼→ POST /api/_signals/ingest ─┐
│ ├→ ring buffer (unstorage)
form captures → /api/v1/webhook ─┘ │ ↓
server errors → signal-errors plugin ──────────────────────────┘ GET /api/_signals/export
?since=<seq>&limit=<n≤2000>
→ { rows, cursor, site }
The envelope — one shape for both kinds:
SignalRow {
id, seq, ts, site,
kind: 'event' | 'log',
name,
severity?, // logs
visitor?, // { hash, class: 'human' | 'agent' | 'bot', device, browser, os, ... } — server-side
page?, referrer?, utm?,
review?, // ?polaris_review=<token> — this page load only, never persisted
data?
}
useEvents() → the signal provider → a debounced batch
queue, flushed with sendBeacon on pagehide.visitor.hash, a
server-side sha256(daily_salt + site + ip + user_agent). The salt rotates at UTC
midnight and the old one is deleted, so a visitor is one value for a day and unlinkable
across days. Nothing is written to the visitor's device and the raw IP is never stored,
so no consent banner is needed for signal capture.ui.click ({ target, label?, section? }), ui.section ({ section, visible }), and ui.page ({ from? }). They are
content-free by construction — authored element identity and labels only, never pixel
coordinates, input values, or keystrokes. Sections are named by data-section or a plain
<section id>; SectionWrapper stamps it for you. data-signal-ignore excludes a subtree.window.onerror, unhandledrejection, Vue errorHandler) land in the
same stream as kind: 'log' rows. So do Nitro request errors, server-side./api/v1/webhook (historical route name — it is the
form-capture endpoint, not a webhook sender), pass honeypot/timing and zod validation, and
land as a form_submitted row. The server is the durable capture point.User-Agent on every ingest path. A
client-supplied value is always overwritten, never trusted.seq is monotonic; consumers send back the last cursor they saw.Pulling the stream:
curl -H "Authorization: Bearer $NUXT_SIGNAL_EXPORT_TOKEN" \
"https://your-site.com/api/_signals/export?since=0&limit=500"
# → { rows: [...], cursor: 512, site: "my-site" }
The endpoint fails closed: with NUXT_SIGNAL_EXPORT_TOKEN unset it returns 503, never open
data. Rows live in useStorage('signals') — memory by default, 100 000 rows, oldest
evicted. Mount nitro.storage.signals to an fs/KV driver if they must survive a restart.
| Surface | What ships |
|---|---|
/api/_health | Unauthenticated liveness+identity check for external monitors (Polaris): { ok, service: 'foundry', version, siteId, timestamp }, Cache-Control: no-store. Pure computation — no storage or content access. |
| MCP tools | list-pages, get-page, what-changed — auto-registered from server/mcp/tools/, served by @nuxtjs/mcp-toolkit. Visit /_mcp/tools in dev to verify. |
llms.txt | /llms.txt + /llms-full.txt ship by default via nuxt-llms (a real layer dependency, zero config); sections auto-populate from your content collections. Override with an llms: config in your app — see examples/foundry/nuxt.config.ts. |
| Raw markdown | The markdown-rewrite module writes Vercel edge redirects so Accept: text/markdown or a curl/* UA on a page URL serves /raw/<path>.md, and / serves llms.txt. No-op off Vercel and in dev. |
| SEO / OG | @nuxtjs/seo (sitemap, robots, schema.org, link checker, canonical redirects) plus a Satori OG image component for landing pages. |
| RSS | Config-driven feeds from any content collection — rss: { feeds: {} } in your app config, served at /rss/{key}. |
Content is YAML and markdown, edited by whoever owns the site — not code. The layer exports
zod schemas you compose in your own content.config.ts:
import { basePageSchema, baseFaqSchema, baseConfigSchema, baseNavigationSchema }
from '@incubrain/foundry/schemas'
content/
├── pages/ Markdown pages (basePageSchema)
├── faq/ FAQ entries (baseFaqSchema)
└── config/
├── site.yml Site config (baseConfigSchema)
└── navigation.yml
baseTeamSchema and bannerSchema are exported too.
Sections are deliberately minimal. The layer ships SectionWrapper — an accessible
UPageSection wrapper that emits a section_view_<id> event on intersection and sets
data-testid. Concrete sections (Hero, Offer, …) live in your app, not the layer: an
opinionated design system is out of scope on principle.
Convert components are the intent-capture surface — the place where a visit becomes a signal worth acting on:
| Component | Captures |
|---|---|
ConvertForm | Email / lead capture → /api/v1/webhook |
ConvertExternal | Outbound clicks (Stripe, LemonSqueezy, Cal.com) |
ConvertInternal | Internal CTA clicks |
ConvertPricing | Pricing interaction |
ConvertSocial | Social profile clicks |
ConvertSocialShare | Share actions |
ConvertRss | Feed subscriptions (rss module) |
Three layouts ship: default, article, landing — selected per route with an
appLayout route rule.
Environment
| Variable | Purpose |
|---|---|
NUXT_SIGNAL_EXPORT_TOKEN | Bearer token for GET /api/_signals/export. Unset → 503. |
NUXT_PUBLIC_SITE_ID | Site identifier stamped on every row. Defaults to the request host. |
NUXT_PUBLIC_SITE_URL | Canonical site URL, consumed by @nuxtjs/seo. |
That is the signal surface in full. A third signal env var means a feature that does not capture signal.
Module options (nuxt.config.ts)
events: {
signals: {
enabled: true, // signal capture on/off
capacity: 170_000, // ring buffer size (14 days)
captureErrors: true, // client errors → the stream
},
debug: true, // echo events to the console in dev
}
layer/ The Nuxt layer — published as @incubrain/foundry
app/ Components, composables, layouts, pages
modules/ events (signal capture), rss, markdown-rewrite, config, css
server/ MCP tools, content API, caching
examples/foundry/ Reference app — the one to copy
playground/ Test harness (unit + nuxt + e2e specs)
deploy/ vercel.website.json (Dockerfile in examples/foundry)
pnpm dev # layer dev server
pnpm dev:foundry # example app
pnpm test # vitest (specs live in playground/test)
pnpm verify # lint + typecheck
pnpm build # build the layer
Releasing is documented in RELEASING.md.
Validation is not product. These are out, and staying out:
MIT