ddcore — reference for agents
ddcore (Data Driven Core) is an application framework in the spirit of Frappe: DocTypes (models declared in TypeScript) become Postgres tables, forms, lists and a REST API automatically. The core is a Go binary (ddcore) embedding esbuild + goja: an app's logic is TypeScript run on the server, synchronously (no await), and its form scripts run in the desk.
Available documents (also as MCP resources ddcore://docs/<name>):
conventions— an app's layout, naming, calling another app's server code, what never to do, and the core/app compatibility contract (version, theddcorerange)fieldtypes— every fieldtype and field propertyauth— sign-in, single sign-on (OpenID Connect providers), sign-in through an app (credential providers: another system's username and password, per tenant), lockout, recovery, invitation, self-service and secretsfield-permissions— field levels (permlevel): confidential fields omitted from every read path and protected on writetenancy— several customers in one database ("tenancy": true): spaces,sharedDocTypes, theSite TenantDocType andonTenantCreate,ddcore tenantand--tenant,ddcore.tenant.*, what a tenant's System Manager may do, how row-level security enforces it, limitsscopes— user access scopes (User Permission): restricting users to companies, units or customers across every read and write pathportal— self-service portals (definePortal): Website Users, identity and match, pages, uploads, limits and inviting portal userswww— an app's public static site under a URL prefix (wwwindefineApp): a SPA build served with a fallback, caching and security headers, the guest methods behind it, tenancy, images and rate limitingsharing— document sharing (Document Share): per-user read/write/share grants on one document, scope override, auditcontroller-api—defineController, hooks, methods, the server'sddcore.*APIform-api—defineForm,frm.*, dialogs, actions on a grid's selected rows,defineListView, its actions on the selected rows and its Calendar, Kanban, Gantt and Card views,defineProfileSection(desk)trees— hierarchical DocTypes (isTree): the parent Link andis_group, write integrity, thedescendants offamily of filters, scopes down a branch and the Desk tree viewvirtual-doctypes— virtual DocTypes (virtual): a read-only union of other DocTypes with a field mapping,<Source>:<id>ids, per-source permissions and Links to either sourcesearch— global search: the Mod+K palette, which DocTypes are searched (globalSearch), ranking and authorizationreport-api—defineReport,defineWorkspace, cards and chartsextending— adding fields to, and overriding properties of, another app's DocTypesmail— mail templates, blocks, attachments and the delivery recordnotifications— persistent event/date rules, authorized recipients, Desk inbox and optional emailassignments— ToDo DocType, assignment workflow, pending work, sidebar widget and due date remindersfeedback— the desk's Feedback dialog: bugs, improvements and feature requests from users, with files and audio notes, filled in with AI when the site has one, landing in the platform spaceworkflows— declarative approval workflows: states, actions, docstatus binding, atomic transitions, server enforcement and Desk action buttonswebhooks— outgoing webhooks: subscriptions, signed delivery after commit, retries, replay and the audit recordi18n— English as the source language, catalogues, Select values, dates and the site timezonemigrations— renames, fieldtype changes, patches and the expand → contract routeupgrade-0.17— moving an app across the 0.17 key rename (name→id): what to rename, what fails silently, how to find itstorage— where uploaded file bytes live (local or S3-compatible), download access and deletionbackup—ddcore backup/restore, maintenance mode, the site version ledger and rollbackexport— exporting a whole DocType, children and attachments, by HTTP or CLIimport— loading an export into a site: identities and metadata kept, effects not replayed, resumable, reconcileddata-import— loading a CSV/XLSX file from the Desk or HTTP: each row a normal insert or update as the user, with a dry run and per-row errorsprint— print templates, print block builders, Letter Head branding, and server-side PDF generationai—ddcore.ai: the site's language model from server code (answers, tool loops, structured JSON, images, transcription) andddcore.test.fakeAipush— Web Push from server code with the site's VAPID key:ddcore.push.send,publicKey()andddcore push keysexternal-db—ddcore.externalDb(name): read-only SQL Server queries from server code, configured fromDDCORE_SECRET_<NAME>_*vault— encrypted credential vault (ddcore.vault.*),Vaultfieldtype, and audit loggingaudit— unified administrative audit events (tab_audit_event), sanitization, immutability, retention and CLI inspectioncli— theddcorecommands and the development loopops— liveness and readiness probes, request correlation, queue signals anddoctorfeature-requests— when a gap belongs in the framework, and how to report it upstream
What changed between releases is not in this list — it is the changelog, served as its own resource ddcore://changelog. The whats_new tool returns the part of it above the version you are running, together with the newest published release; ddcore doctor warns when that release is ahead of your binary. Read it after an upgrade, and before reporting a gap.
Typical flow
- In a monorepo,
ddcore new-app <name>createsapps/<name>; in an app's own repository, useddcore new-app <name> --dir .andapps: ["."]. - Write
doctypes/<snake>/<snake>.doctype.tswithdefineDoctype. ddcore migrate(or the MCPmigratetool) creates and alters the tables and generates, under.ddcore/, the DocType types and the declarations of the embedded SDKs.- Rules in
<snake>.controller.ts; screen scripts in<snake>.form.ts; tests in<snake>.test.ts. ddcore devruns the server with hot reload;ddcore testruns the tests in rolled-back transactions.
Mental model
- Single DocTypes (
isSingle) expose one settings document with a fixedsingletonidentity; seecontroller-api. - One DocType = one table
tab_<snake_case>; child tables (isChild) haveparent,parenttype,parentfield,idx. - Standard columns:
id(PK, text),owner,creation,modified,modified_by,docstatus(0 draft, 1 submitted, 2 cancelled). The key isid(doc.id, filters andfieldsonid);nameis an ordinary fieldname an app may declare. A pre-0.17 database is moved fromnametoidbymigrate— seemigrations; an app written before 0.17 is moved byupgrade-0.17. - Lifecycle:
beforeValidate → validate → beforeSave → (insert|update) → afterInsert/onUpdate;beforeSubmit → onSubmit;beforeCancel → onCancel;onTrash → afterDelete. - The core validates
reqd,unique,uniqueKeys(compound business keys, checked before the write and enforced by a partial unique index, so a race cannot slip through), a Select'soptions, that links exist,fetchFrom,mandatoryDependsOn(on the server), refuses to change a field withoutallowOnSubmitonce submitted, and refuses to change asetOnlyOncefield once it holds a value — on every write path,setValue/dbSetincluded. - One transaction per request or job. An error rolls it back. There is no
commit()for an app. - A rename is declared (
renamedFrom), never inferred, and a fieldtype change that could lose data is refused until you declareconvertor move the data across releases. Seemigrations. - Every user-facing string is English and is a key — a
label:as much as a_("…"). Translations live intranslations/<lang>.csv, and a Select's value is canonical English with a translated label. Seei18n.