AppGen: Application Generator Guide
SDCStudio's AppGen feature generates a standalone Django application from a published data model. The download is a complete project, with Docker configuration, that stores every record as a validated Governed Data Record. For an app of your own in TypeScript on Supabase, the model page also offers a Supabase kit. The applications and the kit are licensed under Apache 2.0.
Overview
The generated application includes:
- A data entry wizard: a step-by-step form built from the model's components, with an exceptional value selector on every field.
- A record model: one Django model per data model that stores each Governed Data Record as XML, with a JSON projection for queries.
- Validation: every record is validated against the model's XSD with the Semantic Data Charter validator before it is stored; recoverable errors are tagged with exceptional values.
- List, detail, download and delete views: records can be browsed, searched, downloaded as XML or JSON, and deleted.
- Bulk import: XML files can be imported from a ZIP archive or a server directory.
- A triplestore: records are extracted to RDF and loaded into Apache Fuseki (Lightweight) or GraphDB (Enterprise).
- A REST API for uploading model files and validating records, documented in the Generated Application API Reference.
- Docker Compose configuration for the whole stack.
Prerequisites
To use AppGen, the following conditions must be met:
- A published model whose package has been generated. The Generate App (choose one) card appears on the model page after Generate.
- 1,500 credits in your wallet for the first download of the model. Generating the build is free, and every later download of the same model is free.
How to Use
1. Create a Data Model
- Navigate to the Data Models section in SDCStudio.
- Create a new Data Model or open an existing one.
-
Add components to define your data structure. The generator reads the model's cluster tree and turns each component into a field on the wizard's data entry step:
Component type Wizard field XdString single-line text input, with the model's minimum length, maximum length and pattern XdToken single-line text input XdBoolean checkbox XdCount integer input, with the model's minimum and maximum XdQuantity decimal input, with the model's minimum and maximum XdFloat, XdDouble number input, with the model's minimum and maximum XdTemporal date input XdOrdinal select list XdLink URL input XdFile file input List types (XdStringList, XdTokenList, XdBooleanList, XdIntegerList, XdDecimalList, XdDoubleList, XdNonNegativeIntegerList, XdPositiveIntegerList) single-line text input Cluster a collapsible card that groups its components Every component field also gets an Exceptional Value selector, and components whose model allows them get inputs for valid time begin, valid time end, time recorded, modified, latitude and longitude. The form marks no field as required; the XSD decides what is required when the record is validated.
-
Publish the model and click Generate so the package exists.
2. Generate the App

- Open the published model's page and scroll to Generate App (choose one), below the Model Generation card.
- Pick Lightweight (PostgreSQL, Fuseki, Redis and Celery, Django's built-in auth; for development and small deployments) or Enterprise (adds GraphDB, SirixDB and Keycloak; needs 6 GB of free RAM) and click its Generate.
- Wait for the build; Retry is offered if it fails.
- Click Download App to fetch the ZIP. Each profile is built and stored separately.
Cost: 1,500 credits for the first Download App of a model, taken at download; every later download of that model by you, either profile or the Supabase kit, is free (a revision of the model is a new model). Since 4.16.0; before that every download was charged. Generating the build is free, and a failed build charges nothing.
From a script or an agent. POST /api/v1/dmgen/dms/<ct_id>/generate_app/ with {"profile": "lightweight", "async": true} (or "enterprise") answers 202 with a job to poll (see the assembly reference); GET .../download_app/ or .../download_enterprise_app/ downloads it and says whether this download was charged. The key needs Spend for a first download. sdcmodeler's generate_app tool does both and refuses until the person has agreed to the charge (see The SDC MCP Servers).
3. Run the Generated App

The ZIP is named {app_name}_sdc_project.zip, where app_name is the model's title in lower case with spaces and hyphens replaced by underscores. It unpacks to sdc_project/sdc4/, which is the Django project.
You need Docker, or Podman with podman-compose. The steps below use docker compose; podman-compose takes the same arguments.
-
Unpack and enter the project:
bash unzip blood_pressure_sdc_project.zip cd sdc_project/sdc4 -
Create the environment file:
bash cp .env.example .envThe file sets the host ports, the default passwords andSECRET_KEY,DEBUGandALLOWED_HOSTS. The defaults are for local use. -
Start the stack:
bash docker compose up -d --buildOn startup thewebservice runsmakemigrations,migrate,create_default_superuser,init_fuseki(Lightweight) orinit_graphdb(Enterprise),collectstatic, and then the development server.create_default_superusercreates the useradminwith passwordadmin123when no user exists.init_fusekiwaits for Fuseki, checks that the dataset configured inmediafiles/fuseki/configuration/answers, and loads the reference model ontologies and the model's RDF schema;init_graphdbwaits for GraphDB, creates the repository if it is missing, and loads the same files. Enterprise's first start takes a few minutes while GraphDB and Keycloak initialize. -
Open the application. The default host ports are:
URL What http://localhost:18000/home page listing every installed data model app http://localhost:18000/{app_name}/the model's record list; New Instance starts the wizard at /{app_name}/wizard/http://localhost:18000/{app_name}/bulk-import/bulk import, with Download XML Template http://localhost:18000/admin/Django admin, including API Keys http://localhost:18000/admin/install-app/the Install SDC4 App page, linked from the home page menu http://localhost:18000/docs/readme/the project README rendered in the browser; also /docs/triplestore/and/docs/license/http://localhost:18000/api/v1/the REST API, mounted in every generated project; see the API reference http://localhost:13030/Fuseki (Lightweight) http://localhost:17200/GraphDB Workbench (Enterprise) http://localhost:18080/admin/Keycloak admin console (Enterprise) https://localhost:19443/SirixDB, self-signed certificate (Enterprise) PostgreSQL is published on
15432and Redis on16379. Every service usesadmin/admin123by default. Every page requires a login: the admin, and the data model pages (see Current Limitations). -
Create more admin users when needed:
bash docker compose exec web python manage.py createsuperuser -
Stop the stack with
docker compose down. Data stays inmediafiles/.
Generated Application Structure
sdc_project/
└── sdc4/
├── manage.py
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── .env.example
├── .sdc-generator-version SDCStudio and reference model versions that built the app
├── README.md
├── LICENSE
├── sdc4/ project settings, urls, views, celery
├── {app_name}/ the data model app
│ ├── models.py the {Model}Instance record model
│ ├── admin.py
│ ├── urls.py
│ ├── views/ wizard, list, detail, bulk_import
│ ├── forms/ setup, context, data_entry, review, bulk_import
│ ├── utils/ wizard_config, wizard_state, xml_builder,
│ │ xml_skeleton.xml, field_mapping.json,
│ │ json_extractor, rdf_extractor, bulk_import
│ ├── templates/{app_name}/
│ ├── templatetags/
│ ├── migrations/
│ ├── fields.py, widgets.py, signals.py, tasks.py, apps.py
├── api/ REST API app
├── core/ management commands, docs views, admin install page
├── generic_storage/ records for models with no installed app
├── sdc4_shared/utils/ validator, DM registry, app installer, triplestore client
├── docs/ TRIPLESTORE_GUIDE.md
├── ontologies/ sdc4.xsd, sdc4.ttl, sdc4-meta.ttl and the OWL files
├── mediafiles/
│ ├── dmlib/ catalog.xml, sdc4.xsd, dm-{ct_id}.* model files
│ ├── postgres/
│ └── fuseki/ Lightweight; Enterprise has graphdb/, sirixdb/, keycloak/
├── import_data/ server-side directory for bulk import
├── static/vendor/
├── staticfiles/
└── templates/ home page, admin install page, docs viewer
mediafiles/dmlib/ holds every published file for the model (dm-{ct_id}.xsd, .xml, .json, .html, .ttl, _shacl.ttl and the others that exist), plus catalog.xml and a local copy of sdc4.xsd so validation resolves the reference model without network access. {app_name}/utils/xml_skeleton.xml is the model's published XML template; the wizard fills it in and prunes what is left empty. When the model was published with README-AI-PROMPT.md and SHACL_README.md, they are copied into docs/.
The project can host more than one data model app. A second model's ZIP is installed with python manage.py install_app <zip> or the Install SDC4 App page, followed by a restart; settings.py and urls.py discover installed apps automatically.
The Supabase Kit
For a TypeScript app on Supabase, the model page offers a third box beside Lightweight and Enterprise: Supabase kit. Click Download Kit to fetch supabase-kit-dm-<ct_id>.zip, a new project's supabase/ directory. Nothing is built ahead: the kit is generated from the published XSD when you download it.
The kit holds:
- The kit itself, under
supabase/functions/_shared/sdc/: one TypeScript file per component, shared by every model that uses it; one file per model, with its record's type in a nested JSON projection; the runtime that builds and projects records; aREADME.md; and aLICENSE(Apache 2.0). - The gate, an Edge Function (
supabase/functions/gate/) and the one way a record is stored. It builds the XML Governed Data Record from the JSON it is sent, validates it against the published model, recovers what an Exceptional Value can cover, and stores it. - Migrations: a base migration once per project (the model registry and the records table, whose write grants are revoked from every role but the gate's), and one per model with its published XSD and a view of its records that obeys the table's row-level security.
Apply it with the Supabase CLI: supabase db push, then supabase functions deploy gate. The app writes by POSTing a record's JSON (or its XML) to the gate and reads JSON from the model's view, public.dm_<ct_id>. The kit's README gives the gate's answers and the read policy to replace with your own.
Several Models in One Project
A project holds as many models as you need. Add each further model's kit to the same project with sdcmodeler, from the command line or as an MCP tool:
sdcmodeler kit <ct_id> --project .
It writes the model's file and only the components the project does not have yet, so a component two models share is one file that both use. It adds one migration numbered after the project's latest, rewrites the model index, and never rewrites a migration. If a component file in the project differs from the published one, it refuses before writing anything. Push the migration and deploy the gate again afterwards. The same files are served at GET /api/v1/dmgen/dms/<ct_id>/supabase_kit/ for a client of your own.
Use the ZIP for a project's first model only: it includes the base migration, which a project applies once.
Current Limitations
- One charge per model, whichever you download. The Lightweight and Enterprise builds are stored separately and the Supabase kit is generated on request; the first download of the model, of any of the three, is charged and every later one is free.
- No edit view. The wizard creates records and the detail page deletes them. There is no route for changing a stored record, and the admin shows records read-only.
- List components render as text. The list types have no dedicated widget; they appear as a single-line text input.
- Sign-in required. Every data model page (wizard, list, detail, downloads, delete) requires a signed-in user, created in the admin; bulk import is for staff users. The Lightweight profile signs in at
/accounts/login/with Django's accounts; the Enterprise profile through Keycloak. - Development defaults. Every service starts with
admin/admin123, Django runs withDEBUG=True, and Enterprise starts Keycloak in development mode. The README's Production Deployment section lists what to change. - Bulk import keeps invalid files. Files that fail validation are still stored, marked
invalid.
Troubleshooting
- The build fails. The card shows the server's message and a Retry button. Nothing is charged for a failed build. The server refuses to build when the model is not published or its package has not been generated.
- Download App returns 402. This is the model's first download and the wallet has fewer than 1,500 credits (a later download is free and never answers 402). The message reads
Insufficient credits. This needs 1,500 credits; you have N.Add funds and try again; see Wallet and Billing. - Enterprise services do not start. The Enterprise stack needs at least 6 GB of memory allocated to Docker or Podman. Check
docker compose logs graphdbanddocker compose logs keycloak. If Keycloak fails on its database, bring uppostgresfirst and thenkeycloak. - Port already allocated. The compose file publishes
18000,15432,16379and13030(Lightweight) or17200,19443and18080(Enterprise). ChangeWEB_PORT,DB_PORT,REDIS_PORT,FUSEKI_PORT,GRAPHDB_PORT,SIRIX_PORTorKEYCLOAK_PORTin.env. - Container name conflicts when two generated projects run at once: set a different
APP_NAMEin each project's.env. - Permission denied on a volume at first start. Fuseki runs as UID 1000 and PostgreSQL as UID 999:
sudo chown -R 1000:1000 ./mediafiles/fusekiandsudo chown -R 999:999 ./mediafiles/postgres. Under rootless Podman usepodman unshare chowninstead. Enterprise usesmediafiles/graphdbandmediafiles/sirixdb(UID 1000). - The triplestore has no data. The init command gives up if the triplestore is not ready in time. Run it again:
docker compose exec web python manage.py init_fusekiorinit_graphdb. - Code changes are not showing. Rebuild the image:
docker compose downthendocker compose up -d --build.