@package-system

Create a package — all three ways

The three ways to ship a Neuralis package (installed node, project WASM, source markdown) — one PackageDefinition, side by side.

In Neuralis every capability is a package; here are the three reach-paths to ship one — the PackageDefinition shape is identical across all three, you only pick where it lives. An installed node package depends on the host and runs in-process; a project package drops into a project's _packages/ and runs WASM-sandboxed; a source package is discovered in a synced filesystem source with no install step at all. Author the same manifest, the same tools/*.json, the same skills/<name>/SKILL.md — the platform routes it differently based on the path it arrived through.

The parity matrix

installed node @scope/*project _packages/ (WASM)source markdown
Installadmin: pnpm neuralis:pkg add + rebuild → node_modules/<name> (any scope)drop a folder in _packages/none — discovered in a synced source
Discoverydeps-scan (the neuralis block)ProjectPackageScannersource vector-index
Trustfirst-party (host-assigned)untrusted → trustedinherits the source trust
Runtime codein-process nodeWASM sandbox (neuralis-build generates dispatch)none — context-only
Tools + handlers✅✅ generated dispatch❌
Routes✅ direct✅ host-gated (dist/routes.json)❌
UIdirect React (a prebuilt module built with neuralis-build ui)iframe only❌
Statelivestateless (lazy-init, ctx.data)❌
Hookscallback executes; script / http are declared but have no executordeclared only — nothing fires today❌
skills / rules / instructions / agents / docs✅✅✅ (whole surface)

The PackageDefinition is the same in every column: the same neuralis manifest block, the same five file categories, the same schema-first tools. What changes is how the host treats the package once it finds it — whether its runtime code runs in-process or sandboxed, whether its UI renders directly or in an iframe, and which contributions it is allowed to carry.

The 3 irreducible differences

Between an installed node package and a project WASM package, only three things genuinely differ — everything else is the same contract:

  1. UI: direct React vs iframe. A first-party package renders its widgets and cards as React components running in the workspace itself — shipped as a prebuilt UI module (app.module) the host attaches at runtime; a project package's UI loads in a sandboxed iframe instead, isolated from the host runtime.
  2. State: live vs stateless. A node package holds live in-process state for the instance's lifetime; a WASM package is stateless across calls — its init(ctx) runs lazily once and durable state persists through the data dir (ctx.data), which the brain sync path chunks, embeds, and makes revertible.
  3. Hooks: in-process callbacks vs nothing that fires. All three handler kinds (callback, script, http) are declared in hooks.json and validated at load, but only callback — an in-process function a node package registers — actually executes. A script or http hook loads cleanly and then fails at dispatch with a no executor configured error, so a project package effectively has no working hook path today. Do not build a capability whose only trigger is a hook.

A source package is the simpler case: it carries no code at all. It is context-only — skills, rules, instructions, agents, and docs — so it has no tools, no routes, no UI, no state, and no hooks. It contributes the whole markdown surface and nothing else.

Which layer does this value belong in?

Three stores exist and they are not interchangeable. Pick by what the value is, not by what is convenient to read:

The value is…Goes inRead with
a secret — an API key, token, or client secretmanifest credentials[]ctx.credentials.resolveCredential(id, scope)
a tunable — a cap, timeout, TTL, interval, threshold, model or URL defaultmanifest configSettings[]the injected config provider, read per call
boot-critical infrastructure — a database URL, an auth secret, a topology address needed before anything is decryptable.envthe host, at boot

Two rules follow from the table and are enforced:

  • A secret is never a config key. Config values are admin-visible plaintext and appear in the Config tab. Putting a token there publishes it.
  • Declare only what something resolves. A credential row an owner can fill in that no code reads is worse than no row — the operator believes the key is configured while the feature stays dark. The same applies to a config key nothing reads. Drift guards assert declarations and consumers match in both directions.

And one that surprises people: there is no credentialFields on a source connector, and no credentials map on the factory context. A connector's secrets are ordinary credentials[] declarations, read through the same resolver as everything else. One vocabulary, one store.

Start from an example

There is one official example per way a package reaches the platform. Read the one that matches your target — each is published to the package registry and readable element by element, so you can pull a single contract instead of a whole tree:

Your package will…ExampleRead it
be admin-installed as an npm dependency@neuralis/example-builtinneuralweb read '@neuralis/example-builtin#docs/docs.create-package'
be dropped into a project's _packages/@example/project-packageneuralweb read '@example/project-package#docs/<id>'
be markdown in a synced source@neuralis-examples/example-sourceneuralweb read '@neuralis-examples/example-source#skill/<id>'

Markdown elements (skills, rules, agents, instructions, docs, workflows, team members) read as plain text — one request, an optional --path for a multi-file element and an fs-read-style --line-start/--line-end window; the tarball bundle exists too, but it is the install rail, not the read rail. Tool, connector, credential and config-setting contracts travel as a JSON document instead: neuralweb info '<identity>#tool/<id>'. The full picture, including what the examples do and do not promise, is on Contract examples; the registry itself is on the marketplace page.

Where to go for depth

On this page