CoreAPI Reference
The 46 methods on the Go CoreAPI surface, grouped by domain.
The CoreAPI surface
The CoreAPI is the single Go interface that exposes every kernel capability to plugins, scripts, and the MCP server. It's defined in internal/coreapi/api.go as a Go interface with 56 methods across 16 domains. Three adapters wrap that one interface:
- Internal Go callers — kernel handlers in
internal/cmsimport the implementation directly. The capability guard is bypassed; kernel code is trusted. - gRPC
SquillaHostservice — the wire surface compiled-Go extensions speak. Defined inproto/coreapi/squilla_coreapi.proto; generated client inpkg/plugin/coreapipb. Every call passes throughcapabilityGuardon the server side. - Tengo modules — the script-side surface. Each domain gets a
core/<name>module ininternal/coreapi/tengo_*.go. Same capability guard, plus the script sandbox.
The MCP server is a fourth caller, but it dispatches through the internal Go path with a typed internal-caller context, so the capability guard does not enforce token-level permissions for MCP. Token-level enforcement happens via the scope gate in MCP dispatch (read | content | full) before the tool handler is invoked.
read token cannot create nodes regardless of which CoreAPI capabilities its underlying user has, and a full token can do anything regardless of which capabilities the kernel asserts on the path. Pick scopes carefully when issuing tokens.Domains and methods
Nodes (5)
GetNode(ctx, id)→ Node —nodes:readQueryNodes(ctx, query)→ NodeList —nodes:readCreateNode(ctx, input)→ Node —nodes:write; emitsnode.createdasync after commitUpdateNode(ctx, id, input)→ Node —nodes:write; emitsnode.updatedDeleteNode(ctx, id)→ error —nodes:delete; emitsnode.deleted
Node Types (5)
RegisterNodeType(ctx, input),UpdateNodeType,DeleteNodeType—nodetypes:writeGetNodeType,ListNodeTypes—nodetypes:read
Taxonomies (5) + Terms (6) + ListTaxonomyTerms (1)
- Taxonomies —
RegisterTaxonomy,UpdateTaxonomy,DeleteTaxonomy(nodetypes:write);GetTaxonomy,ListTaxonomies(nodetypes:read). - Terms —
ListTerms,GetTerm(nodes:read);CreateTerm,UpdateTerm(nodes:write);DeleteTerm(nodes:delete). ListTaxonomyTerms(ctx, nodeType, taxonomy)→ []string — thin convenience read used heavily by editor autocomplete.
Settings (6)
GetSetting,GetSettings(prefix),GetSettingLoc,GetSettingsLoc—settings:read. TheLocvariants take an explicit locale; the others use the request's resolved language.SetSetting,SetSettingLoc—settings:write; emitssetting.updatedwith{key, language_code, value}after commit. Sensitive-shaped keys are auto-encrypted.
Events (2)
Emit(ctx, action, payload)→ error —events:emit. Fire-and-forget; the bus dispatches in a goroutine.Subscribe(ctx, action, handler)→ UnsubscribeFunc —events:subscribe. The handler is invoked with each matching event payload until the returned function is called.
Email (1)
SendEmail(ctx, req)→ error —email:send. Internally fans out via theemail.sendrequest-style event usingPublishRequest; provider extensions subscribe and the first non-nil error wins. If no extension declaresemail.provider, the call fails fast with "no provider".
Menus (6)
GetMenu,GetMenus—menus:readCreateMenu,UpdateMenu,UpsertMenu—menus:write.UpsertMenuis idempotent on slug — use it from seed scripts.DeleteMenu—menus:delete
Routes (2)
RegisterRoute(ctx, method, path, meta),RemoveRoute(ctx, method, path)—routes:register
Filters (2)
RegisterFilter(ctx, name, priority, handler)—filters:register; returns anUnsubscribeFuncthat removes only this handler from the chain.ApplyFilters(ctx, name, value)→ any —filters:apply; passesvaluethrough every registered handler in priority order, returns the post-chain value.
Media (4)
UploadMedia(ctx, req)—media:writeGetMedia(ctx, id),QueryMedia(ctx, query)—media:readDeleteMedia(ctx, id)—media:delete
All four route through whichever active extension declares provides: ["media-provider"]. The bundled media-manager fills the slot; an S3-style provider can replace it by activating with a higher priority.
Users (2, read-only)
GetUser(ctx, id),QueryUsers(ctx, query)—users:read
Scripts and extensions cannot create, update, or delete users via the CoreAPI. User mutations live behind admin-authed handlers in the kernel.
HTTP (1)
Fetch(ctx, req)→ FetchResponse —http:fetch. Outbound HTTP from extensions and scripts; default 30-second timeout, configurable per-call. Honors a kernel-side allow-list and blocks private network ranges by default to prevent SSRF.
Log (1)
Log(ctx, level, message, fields)→ error —log:write. Levels:info,warn,debug,err. Output lands in the kernel's structured log stream with caller info auto-prefixed.
Data Store (6)
DataGet(ctx, table, id),DataQuery(ctx, table, query)—data:readDataCreate(ctx, table, data),DataUpdate(ctx, table, id, data)—data:writeDataDelete(ctx, table, id)—data:delete(each capability is enforced strictly; there is no fallback fromdata:deletetodata:write)DataExec(ctx, sql, args...)— internal-only. Not exposed through the gRPC client interface, so extensions cannot call it. The MCPcore.data.exectool is a separate path, env-gated bySQUILLA_MCP_ALLOW_RAW_SQL=true(case-insensitive). Use sparingly.
File Storage (2)
StoreFile(ctx, path, data)—files:writeDeleteFile(ctx, path)—files:delete
Capability matrix
The kernel defines 27 capability strings across the 14 enforcement domains, plus admin_access as a separate gate used by extension admin proxy routes. Internal callers bypass the guard; everyone else must declare what they intend to use. The full set:
- nodes — read, write, delete
- nodetypes — read, write
- settings — read, write
- events — emit, subscribe
- email — send
- menus — read, write, delete
- routes — register
- filters — register, apply
- media — read, write, delete
- users — read
- http — fetch
- log — write
- data — read, write, delete
- files — write, delete
- admin_access — separate; gates extension admin proxy routes (extension-level, not Tengo).
The gRPC adapter rejects calls outside the declared set with a typed error code so callers can branch on it. The Tengo adapter wraps the same error as a string-typed result that the script can check with is_error.
Data store table scoping
The data-store CoreAPI applies two layers of access control on top of the capability guard. Both layers must pass for the call to succeed; either failing returns a typed error.
- Hard-coded kernel-private deny list. The kernel reserves these tables for itself:
users,sessions,password_reset_tokens,roles,role_capabilities,audit_log,schema_migrations,site_settings,mcp_tokens,mcp_audit_log,mcp_token_audit,languages. Denied unconditionally for non-internal callers, regardless of anydata_owned_tablesdeclaration. - Per-extension allowlist. The manifest's
data_owned_tablesarray. Every read/write/delete checks that the requested table is in the calling extension's owned list. Tables outside both lists (i.e. owned by another extension) are denied.
MCP tools run as internal callers and bypass both layers — a token with full scope can therefore touch anything via core.data.* tools. A token with content scope can use the data-store tools but is gated to extension-owned tables (the kernel-private deny list still applies).
Raw SQL
The MCP tool core.data.exec runs arbitrary parameterised SQL statements against the database. It's protected by two checks: the token must be scope full, and the kernel must have SQUILLA_MCP_ALLOW_RAW_SQL=true set (matched case-insensitively). When the env flag is unset, the tool is registered but every call returns "raw SQL access not enabled". The CoreAPI DataExec method behind it is internal-only — it's never reachable from a gRPC plugin or Tengo script regardless of capabilities, so the only path to raw SQL from outside the kernel is the MCP tool with both gates open.