npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

inistate-core

v0.3.5

Published

Free, local runtime for FACTSOps workflows — the deterministic floor of the Inistate stack. Runs governed state machines on persistence you control (SQLite by default), driven over MCP.

Downloads

614

Readme

inistate-core

Make any data source agent-operable — locally, deterministically, on a context budget small enough for a local LLM.

inistate-core is a free, local runtime that lets any MCP-compatible agent drive governed state-machine workflows on persistence you control. One command, zero config, no account, no token. Point an agent at your SQLite file, your spreadsheet, or your Notion database, and it can create records and move them through only the transitions you allow — validated, atomic, and impossible to corrupt into an illegal state.

npx inistate-core

That single command is a complete deployment.


Why inistate-core

It runs on your own data, on your own machine. The default is a zero-config local SQLite store — no account, no token, no network, nothing leaves your laptop. Or run a governed workflow directly on top of data you already have: Notion, Airtable, Sheets, Excel, CSV, SQLite.

It won't blow up your context window. The agent sees a small, fixed set of generic toolslist_modules, get_form, submit_activity, and a handful more. Adding a tenth module, or a tenth backend, adds zero new tools. There's no per-entity tool sprawl, so the surface your agent loads stays constant whether you run one workflow or a hundred. Cost scales with what a task touches, not with how big your system is.

It works with small and local LLMs. Because the engine — not the model — decides what's legal, the model's job shrinks to "pick from this short list, fill these typed fields, retry on a precise error." That's well within reach of a small or locally-hosted model. Pair local storage with a local model and you get a fully local, zero-API-cost, end-to-end-private agentic workflow. (Operating existing workflows is the sweet spot; designing new schemas from scratch still rewards a stronger model.)

It gives away the hard parts. Atomic transitions, illegal-move rejection, idempotent retries, optimistic concurrency, crash durability — the correctness machinery you'd otherwise hand-roll and get subtly wrong. Each is property-tested, not just example-tested. You build workflows; Core guarantees they can't break.

It's free forever, and genuinely useful — not a trial. A team can run real production on local Core indefinitely and never pay. When you eventually need people, roles, UI, and a governed history of who changed what, the upgrade is a backend swap, not a migration: same schema, same tools, re-pointed.

It's the missing combination, packaged. State machine + typed forms + MCP + your own persistence. You'll recognize every piece. What's new is that they finally work together, with the guarantees, behind one install.


What it actually does

Core enforces the present state of your records — only legal transitions, validated forms, atomic writes — and nothing more. It is deliberately free of authorization, escalation, and history.

Core manages the present state of your records. The Inistate Platform manages the governed history of how that state changed — together with the identity, authorization, and escalation a governed history implies.

That split is physics, not a feature gate. Enforcing the present state needs only a record's current value — any store can hold that, which is exactly why Core can run on your own data. Recording an accountable, append-only, actor-attributed past needs a governed store a plain table doesn't have. So Core holds no history of any kind — and that is the point.


Quickstart — win the first 30 seconds

npx inistate-core

A complete deployment: a zero-config SQLite store at ~/.inistate/core.db, serving the Inistate MCP tool surface over stdio.

Wire it into your MCP client with one command — it detects your client and writes the config for you:

npx inistate-core setup

Detects Claude Desktop, Claude Code, Cursor, VS Code, Codex, Windsurf, Cline, and Gemini CLI. Press Enter through every prompt for a working local SQLite deployment.

Prefer to wire it by hand? Add this to any MCP client's config:

{
  "mcpServers": {
    "inistate": {
      "command": "npx",
      "args": ["-y", "inistate-core"]
    }
  }
}

No token. No workspace. Point your agent at it and design a workflow, create entries, and drive transitions — all on local storage.


Editions

| | Inistate Core | Inistate Core Plus | Inistate Platform | |---|---|---|---| | Price | Free | $19.99 one-time | Paid (teams) | | For | make one data source AI-native | make your whole stack AI-native | govern human-AI operations | | Connectors | full connector support — one backend type at a time | multiple backend types at once | managed sync | | Also | deterministic runtime, valid transitions, typed forms | all Core + cross-source workflow modules, presets | people, roles, audit, UI, confidence/intention gating |

Core makes one persistence layer AI-native. Core Plus connects many. Platform governs the organization.

  • Free — make one kind of data source AI-native (any number of files/tables of that kind, e.g. several Excel workbooks).
  • $19.99 once — mix multiple kinds of source at once: Sheets, Notion, Airtable, SQLite, CSV, Excel, or your own adapter, side by side.
  • Platform — make the whole organization governed.

Core is not crippled — it runs the full primitive on one backend type (and unlimited resources of that type). Core Plus is a one-time unlock for mixing backend types: set INISTATE_CORE_PLUS_KEY and map modules with INISTATE_CORE_CONNECTIONS (see Connecting multiple tools). A single backend type never needs a license.


What's inside the boundary

| In Core (free, local) | In the Inistate Platform (paid) | |---|---| | Schema interpretation | Human collaboration, web/mobile UI | | Present-state enforcement (legal transitions) | Visual designer + app generation | | Typed activity forms + validation | Auth / roles, files | | Module design + validation | Governed history & audit trail | | Persistence-agnostic storage | Confidence / intention gating | | Descriptive ai_hint (describe, never govern) | Workspaces, the integrated AI Teammate |

The reliability invariants are the product

Core gives away the hard parts so you don't hand-roll them. Each is property-tested, not just example-tested:

  • Atomic transitions — field writes and the state change commit together or not at all.
  • Illegal-transition rejection — no accepted operation can move an entry into a state its flows don't permit. (This is the single most important guarantee.)
  • Idempotency — replaying a submission with the same idempotencyKey applies the change at most once.
  • Optimistic concurrency — concurrent writers: exactly one wins; the other gets CONFLICT, no lost update.
  • Crash durability — a committed transition survives a process crash and restart (SQLite WAL).

Why it stays light (context + local models)

The architecture is what makes the small-context and local-LLM claims true, not marketing:

  • Generic verbs, not per-entity tools. Every workflow is driven by the same fixed tool set; the module name is a parameter, not a new tool. The surface never grows with your system.
  • Progressive disclosure. list_modules returns a tiny index; schema, forms, and entries are fetched only when needed, and only the slice needed. Each step's context is small by construction.
  • The engine carries the reasoning. Legal transitions are computed and handed to the agent as available activities — the model never has to figure out what's allowed. That's why a constrained, modest model succeeds where an open-ended task would need a frontier one.

The tool surface

Core serves the identical Inistate MCP tools. The runtime set is always on; module-design tools appear after switch_mode("configure").

Served on Core (Both): list_modules, get_module_schema, get_module_canvas, list_entries, get_entry, get_form, submit_activity, submit_activities, design_workflow, validate_design, create_module, update_module, switch_mode.

Platform-only tools (login, list_workspaces, set_workspace, get_entry_history, upload_file, download_file, request_upload_url, confirm_upload) are not silently absent — calling one returns a structured capability message so your agent can explain the gap rather than fabricate a result:

{
  "error": "capability_unavailable",
  "backend": "core",
  "capability": "governed_history",
  "message": "Governed history is not available on the local runtime. A local store keeps the current state of each record, not an accountable, append-only record of how it changed.",
  "upgrade": "Connect to the Inistate Platform to enable governed history."
}

submit_activity also accepts two reliability controls Core honours: idempotencyKey (safe retries) and expectedVersion (optimistic concurrency, read version from get_entry).


Configuration

Everything has a working default — a bare inistate-core is a valid deployment.

| Setting | Env var | Default | Notes | |---|---|---|---| | Database | INISTATE_CORE_DB | ~/.inistate/core.db | SQLite path, memory, or <scheme>://… for a registered adapter | | Multi-connection | INISTATE_CORE_CONNECTIONS | — | JSON map module → target. Mixing multiple backend types — needs Core Plus | | Core Plus license | INISTATE_CORE_PLUS_KEY | — | Unlocks mixing multiple backend types at once (one-time $19.99) | | Custom adapter | INISTATE_CORE_ADAPTER | — | Module that registers your own persistence (see below) | | Server mode | INISTATE_MCP_MODE | runtime | runtime or configure | | Logging | INISTATE_LOG | on | Set to 0 to silence stderr JSON logs | | Notion token | INISTATE_NOTION_TOKEN | — | Required for notion:// targets | | Airtable token | INISTATE_AIRTABLE_TOKEN | — | Required for airtable:// targets | | Google token | INISTATE_GOOGLE_TOKEN | — | OAuth2 access token, required for sheets:// targets |

Storage schemes & connectors

Selected by the scheme of INISTATE_CORE_DB. Built-in backends are dependency-free; standard connectors run a governed workflow on your existing data.

| Scheme | INISTATE_CORE_DB example | Needs | Class / tier | |---|---|---|---| | sqlite (default) | ~/.inistate/core.db, or any file path | — | Built-in — full present-state, durable (SQLite WAL) | | memory | memory | — | Built-in — non-durable (tests/fallback) | | csv | csv://./tasks.csv | — | Built-in (local file) — exposure tier (§5.5) | | excel | excel://./book.xlsx?sheet=Sheet1 | optional exceljs | Standard (local file) — exposure tier (§5.5) | | notion | notion://<databaseId>?module=Tasks&state=Status | INISTATE_NOTION_TOKEN | Standard — exposure tier (§5.5) | | airtable | airtable://<baseId>/<table>?module=Tasks&state=Status | INISTATE_AIRTABLE_TOKEN | Standard — exposure tier (§5.5) | | sheets | sheets://<spreadsheetId>/Sheet1?state=Status | INISTATE_GOOGLE_TOKEN | Standard — exposure tier (§5.5) |

Postgres is on the roadmap. Any other <scheme>://… resolves through a custom adapter you register with INISTATE_CORE_ADAPTER (community/custom tier — see Bring your own persistence). All standard connectors are available in Core; see Notion & Airtable and examples/connectors.md for every connector's full config.

Connecting multiple tools — Core Plus

Core runs one backend type at a time — any number of resources of that kind (e.g. several Excel workbooks) is free. To connect multiple kinds of backend/webservice at once — routing different modules to different tools (e.g. NEC on Notion, Tasks in a CSV, Leads in Airtable) — unlock Inistate Core Plus (one-time $19.99): set INISTATE_CORE_PLUS_KEY and map modules with INISTATE_CORE_CONNECTIONS:

INISTATE_NOTION_TOKEN=ntn_… \
INISTATE_CORE_PLUS_KEY=<your core-plus key> \
INISTATE_CORE_CONNECTIONS='{"NEC":"notion://<dbId>?state=Status","Tasks":"csv://./tasks.csv","Leads":"airtable://<base>/<table>"}' \
npx inistate-core

Each module gets its own connection, all in one runtime. Multiple resources of a single backend type — several Notion databases, or two Excel workbooks — count as one connector and stay free; the gate only fires when you mix different kinds of backend. Without a license, a mixed-type config prints a clear upgrade message and refuses to start (single-type mode is unaffected).


Bring your own persistence

The PersistenceAdapter interface is the open seam: SQLite (default) and an in-memory store ship built in, and you can plug in your own — a team Postgres, or an external substrate like Notion / Airtable / a spreadsheet — without forking. The engine owns correctness; your adapter owns storage. The one hard rule: transaction(fn) must be all-or-nothing, or the reliability invariants can't hold on your store.

For the binary — point INISTATE_CORE_ADAPTER at a module that registers an adapter against a URL scheme, then select it with INISTATE_CORE_DB:

INISTATE_CORE_ADAPTER=./my-adapter.js \
INISTATE_CORE_DB=mystore://./data \
npx inistate-core

Your module exports either register(api) or a default { scheme, create }:

// my-adapter.js
export function register(api) {
  api.registerAdapter({
    scheme: "mystore",
    description: "My durable store",
    create: (target) => new MyAdapter(target),
  });
}

Embedding Core in your own Node process:

import { createRuntime, loadConfig, registerAdapter } from "inistate-core";
import type { PersistenceAdapter } from "inistate-core/adapter";

registerAdapter({ scheme: "mystore", create: (t) => new MyAdapter(t) });
const runtime = createRuntime(loadConfig());
// runtime.backend → wire into createServer(); runtime.engine → drive directly

Implement PersistenceAdapter from inistate-core/adapterapplyQuery and makeDocumentId are exported so your adapter inherits identical query/document-id semantics (and passes the shared conformance suite). A complete, copy-pasteable reference adapter that persists to a single JSON file with atomic commits is in examples/json-store-adapter.ts.

PersistenceAdapter contract

| Method | Description | |---|---| | init() | Create tables / run migrations / hydrate cache. Called once at startup. May be async. | | close() | Release connections / flush pending writes. May be async. | | getModule(name) | Return a stored module by name, or null. | | listModules() | Return all stored modules. | | putModule(m) | Upsert a module definition. | | renameModule(old, new) | Re-key a module and all its entries. | | getEntry(module, id) | Return one entry by id, or null. | | queryEntries(module, q) | Filter/sort/paginate entries — use the exported applyQuery helper to match built-in semantics. | | getIdempotent(key) | Return the cached result for an idempotency key, or undefined. | | transaction(fn) | Must be all-or-nothing. Pass a Tx handle to fn; if fn throws, roll back all writes. The Tx handle exposes getEntry, insertEntry, updateEntry, deleteEntry, and recordIdempotent. |

The one hard rule: transaction must be atomic or the engine's reliability invariants cannot hold. Everything else (sync vs async reads, in-memory cache vs direct DB, connection pooling) is your adapter's business.

Validate your adapter against the shared conformance suite before wiring it in:

import { testAdapter } from "inistate-core/test";
testAdapter(() => new MyAdapter(...));

Notion & Airtable (built-in, exposure tier)

Core ships reference adapters that run governed workflows on top of your existing Notion databases or Airtable tables — the friendliest on-ramp: keep your board, watch the engine block an illegal move on your own data. One Core instance can back many modules, each bound to its own table.

# Notion — single database → one module (each page is an entry; a Status property holds the state)
INISTATE_NOTION_TOKEN=secret_xxx \
INISTATE_CORE_DB="notion://<databaseId>?module=Tasks&state=Status" \
npx inistate-core

# Notion — multiple databases → multiple modules
INISTATE_NOTION_TOKEN=secret_xxx \
INISTATE_CORE_DB="notion://?LeaveRequests=<db1>&Expenses=<db2>" \
npx inistate-core
#   …or map them with INISTATE_NOTION_DATABASES='{"LeaveRequests":"db1","Expenses":"db2"}'

# Airtable — single table → one module
INISTATE_AIRTABLE_TOKEN=pat_xxx \
INISTATE_CORE_DB="airtable://<baseId>/<tableIdOrName>?module=Tasks&state=Status" \
npx inistate-core

# Airtable — many tables in one base → many modules
INISTATE_AIRTABLE_TOKEN=pat_xxx \
INISTATE_CORE_DB="airtable://<baseId>?Tasks=<tbl1>&Leads=<tbl2>" \
npx inistate-core
#   …or map them with INISTATE_AIRTABLE_TABLES='{"Tasks":"tbl1","Leads":"tbl2"}'

Create each Core module with the same name you mapped (create_module({ name: "LeaveRequests", … })); that module's entries then live in its bound table. They use your own credentials (opt-in, never the default). How they work, honestly: Core hydrates each substrate's rows into a per-module in-memory cache at startup, enforces present-state against that cache (so illegal transitions are rejected through Core), and mirrors committed changes back to each table with a best-effort, ordered write-back — failures are logged, never hidden.

What's stored where. Entry records live in the substrate (Notion/Airtable). The things a table can't express — your module definitions (states, activities, flows) and the idempotency map — persist in a small SQLite meta sidecar by default (~/.inistate/<scheme>-<id>.db, one per database/base), so a restart rehydrates records from the substrate and reloads the schema from SQLite without re-creating the module. Override the location with ?meta=<path>, or use ?meta=memory for an ephemeral meta store.

Schema changes are additive. Your module's information fields + the state column are a subset of the connecting table's columns. create_module/update_module reconcile that add-only: a new field adds a column to the substrate (a Notion property via PATCH /databases, or an Airtable field via the Metadata API — which needs a PAT with schema.bases:write), and missing select options are merged in. Core never drops a column — removing a field from the schema just stops mapping to it; the column stays in your table, untouched. Provisioning is best-effort (a failure, e.g. a missing Airtable scope, is logged, not fatal — enforcement still holds against the cache). Disable it entirely with ?provision=off to leave your table structure alone.

Build your own substrate adapter by implementing a small async SubstrateDriver (list / create / update / remove, plus optional add-only ensureColumns) and reusing ExternalSubstrateAdapter — see src/storage/external/.

Two honest guarantee tiers (§5.5). Core's own store (SQLite/Postgres) gives a full present-state guarantee, including against out-of-band corruption within the transactional boundary. An external substrate (Notion/Airtable/Sheets) holds enforcement through Core, but out-of-band hand-edits are possible — so the guarantee softens to "no illegal move through Core". Label that tier; attribute the limit to the substrate.


Scaffold a schema from an existing table

Already have a Notion database, Airtable table, or SQLite table? Generate a starter FACTSOps module schema from it — columns become typed fields, and a detected status column becomes states:

inistate-core scaffold notion://<databaseId>                      # needs INISTATE_NOTION_TOKEN
inistate-core scaffold airtable://<baseId>/<table>                # needs INISTATE_AIRTABLE_TOKEN
inistate-core scaffold ./core.db --table leave_requests           # local SQLite, no creds
inistate-core scaffold excel://./book.xlsx --table Sheet1         # local Excel, no creds

# pipe the schema straight to a file (guidance prints to stderr, schema to stdout)
inistate-core scaffold ./core.db --table leave_requests --name "Leave Requests" > module.json

It prints a ModuleSchema to stdout and a validation summary + next steps to stderr. Override with --name <Module> and --state <Column>. Core deliberately leaves activities/flows empty — it can't infer the governed transitions a plain table doesn't express; defining those is the value Core adds. Finish the schema (e.g. with design_workflow for a pattern), create_module, then point Core at the same substrate and illegal moves are rejected.

The same thing is available to an agent over MCP as the scaffold_module tool (configure mode — switch_mode to configure), designed so the user and the agent design the schema together. One container (a SQLite database, an Airtable base, a Notion workspace) often holds several tables — so the tool discovers them first and you map the chosen ones, each as its own module:

  1. Discover — call with just source pointed at the container: a SQLite database path, airtable://<baseId> (no table), or a bare notion://. It returns { discovery: true, tables: [{ name, id?, columns, rows?, hasState, scaffold_source }] } so the agent can show what's there and ask the user which one(s) to model.
  2. Draft — call again with a chosen table's scaffold_source as source (or a SQLite table), plus optional name / state. It returns { schema, validation, suggestions } — columns become typed fields, a detected status column becomes states.
  3. Refine — confirm the inferred field types with the user, then define activities/flows (e.g. via design_workflow).
  4. Createvalidate_design → create_module, then point Core at the same data.

Network targets read INISTATE_NOTION_TOKEN / INISTATE_AIRTABLE_TOKEN from the environment — tokens are never passed as tool arguments. Listing an Airtable base's tables uses the Metadata API, which needs the token's schema.bases:read scope; single-table scaffolding does not.

Programmatic equivalent: import { introspectSqlite, introspectExcel, scaffoldModule } from "inistate-core" (or introspectTarget to dispatch by URI).


Upgrading to the Platform

npx inistate-core connect

Moving to the Inistate Platform is a backend swap, not a migration — the same schema, the same MCP tools, re-pointed from the local engine to the Platform API. You gain people, UI, governed history, authorization, and the Teammate. Your workflows, vocabulary, and agent integrations carry over unchanged. A team can run real production on local Core forever and never pay — that is intended.


Development

npm install      # installs deps and builds
npm run build    # tsc → build/
npm test         # vitest: invariants, conformance, capability, boundary, durability, end-to-end MCP
npm run scan     # forbidden-symbol boundary scan over the engine + storage
npm run check    # build + scan + test (the full gate)

The engine and storage adapters are kept clean of governance/UI/history concepts by a forbidden-symbol scan enforced in CI (scan). The shared conformance suite runs the same transition scenario against every storage backend (including a developer-style custom adapter) and asserts identical outcomes — the guard against semantic drift.


License

inistate-core is free to use but not open source. It ships under a proprietary free-use EULA (see LICENSE): run it anywhere, forever, at no cost — including in production — but the engine source is closed and no patent rights are granted. The embedded Inistate schema and MCP server are separately published under MIT.

© GNEY SOFTWARE PTE LTD.