SDK generation
The SDK plugin generates a typed package from the database's stored definitions. While the database is open, it updates that package in the configured output directory.
The generated headers call the C ABI and provide typed access to stored definitions.
Enable sdk in the plugin selection at open, with the output settings below.
Calls without it report the unavailable status.
Query results recursively omit fields containing empty objects or arrays.
Generated rows keep zero initialization for absent fields, such as SD_SDK_NULL for an sd_sdk_value field.
Null, empty strings, false, zero and array positions stay intact in the returned document.
Native time values
Generated native time fields use
struct timespec for timestamps and signed durations.
The binding header bindings/c/stardust_time.h supplies checked tick and text conversions that the SDK uses.
stardust_time_from_ticks and stardust_time_to_ticks use normalized nanoseconds, including for negative durations.
The explicit tick conversions refuse precision loss and values outside the target range. These conversions perform no native operation.
A native time schema requires one string member, #utc or #dur, and sets additionalProperties to false.
For example, this schema describes a native timestamp:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 74797065 | txt "type" |
| 09 | 6C 737472696E67 | txt "string" |
| 10 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1A | 4C 23757463 | txt "#utc" |
| 1F | 0F 0A 1A000000 1000 0000 | map leaf, size 10: @1A → @10 |
| 29 | 4C 23757463 | txt "#utc" |
| 2E | 0E 0D 00 0100 010000 00 29000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @29 |
| 3B | AC 70726F70657274 696573 | txt "properties" |
| 46 | 0F 0A 3B000000 1F00 0000 | map leaf, size 10: @3B → @1F |
| 50 | 8C 72657175697265 64 | txt "required" |
| 59 | 0F 0A 50000000 2E00 0000 | map leaf, size 10: @50 → @2E |
| 63 | 14 14 616464697469 6F6E616C50726F70 657274696573 | txt "additionalProperties" |
| 79 | 01 | bit false |
| 7A | 0F 0A 63000000 7900 0000 | map leaf, size 10: @63 → @79 |
| 84 | 4C 74797065 | txt "type" |
| 89 | 6C 6F626A656374 | txt "object" |
| 90 | 0F 0A 84000000 8900 0000 | map leaf, size 10: @84 → @89 |
| 9A | 07 16 A8040000 4600 0000 59000000 7A00 0000 90000000 | map branch, size 22, depth 0 (root), slots 3, 5, 7, 10: @46 → @59 → @7A → @90 |
| B0 | 9A000000 00000000 | root → @9A; previous: none |
Use #dur in both places for a duration schema.
Schema fields support lists and stored-schema references.
Scalar time fields also type query filters and rows.
Native time expression results use the same binding types.
An ordinary string schema stays text, including strings with a date-time format.
Native ticks are signed 64-bit counts of 100 ns.
Timestamp ticks count from the Unix epoch in UTC. Duration ticks can be negative.
Runs
The plugin generates once at open, and again after commits that change definitions. Data commits do not start a run. A run reads all definitions from one committed snapshot.
Each successful render replaces the output directory's contents, including the startup run. See file ownership before choosing an output directory.
Call status to inspect these fields:
| Field | Contents |
|---|---|
enabled | Whether the plugin is enabled |
pending | Whether generation remains |
runs | Run count |
fingerprint | Generation model fingerprint |
outputs | Outputs, each with language, directory and file counts written and removed |
diagnostics | Diagnostics with severity, code, definition type and path, pointer, member, language and message |
failure | Last run failure, with code and message; absent after a successful run |
When to call sync
Generation runs automatically at open and after definition commits, according to the debounce settings. Opening the database or committing definitions can finish before the generated files are ready. Call sync before compiling or reading files that must include those definitions. Applications that can use the files after automatic generation finishes do not need an explicit sync.
The call requests a render immediately, bypassing debounce. It also rebuilds files when definitions have not changed.
Its options accept only the common maxWait option. A timeout leaves the requested run active.
Repeated waits after a timeout join that run. Inspect status when generation reports failed.
Without an output language, sync validates the model inline and writes no files.
Settings
Set c.dir to a dedicated output directory. Add that directory and the directory
of the reflected stardust.h header to your compiler's include paths. Link the
Stardust library as described in the C linking instructions.
Keep application source outside the output directory.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 3C 646972 | txt "dir" |
| 08 | AC 67656E2F70656F 706C65 | txt "gen/people" |
| 13 | 0F 0A 04000000 0800 0000 | map leaf, size 10: @4 → @8 |
| 1D | 4C 23647572 | txt "#dur" |
| 22 | 7C 5054302E323553 | txt "PT0.25S" |
| 2A | 0F 0A 1D000000 2200 0000 | map leaf, size 10: @1D → @22 |
| 34 | 4C 23647572 | txt "#dur" |
| 39 | 4C 50543253 | txt "PT2S" |
| 3E | 0F 0A 34000000 3900 0000 | map leaf, size 10: @34 → @39 |
| 48 | 8C 6D61785F776169 74 | txt "max_wait" |
| 51 | 0F 0A 48000000 3E00 0000 | map leaf, size 10: @48 → @3E |
| 5B | 8C 6465626F756E63 65 | txt "debounce" |
| 64 | 0F 0A 5B000000 2A00 0000 | map leaf, size 10: @5B → @2A |
| 6E | 1C 63 | txt "c" |
| 70 | 0F 0A 6E000000 1300 0000 | map leaf, size 10: @6E → @13 |
| 7A | 07 12 05080000 5100 0000 64000000 7000 0000 | map branch, size 18, depth 0 (root), slots 0, 2, 11: @51 → @64 → @70 |
| 8C | 3C 73646B | txt "sdk" |
| 90 | 0F 0A 8C000000 7A00 0000 | map leaf, size 10: @8C → @7A |
| 9A | 90000000 00000000 | root → @90; previous: none |
Relative paths start at the process working directory when you open the database.
Call stardust_db_plugin_call with plugin "sdk" and operation "sync" before compilation to wait for committed definitions.
The C headers
sdk.h contains the common typed conversion and ownership functions. schemas.h
contains schema structs and shared list types. queries.h, mutations.h and
expressions.h contain the stored operations of each definition type.
Each header exposes the same model fingerprint through its SD_*_MODEL_FINGERPRINT macro.
C11 headers use static inline functions, so applications can include them in multiple translation units.
Names have a definition-type prefix: sd_schema_, sd_query_, sd_mutation_
and sd_expression_. Functions and fields use snake_case. Original wire names
stay in generated conversion descriptors. Schema and operation names can match
because their prefixes differ. Name collisions fail before publication.
The plugin preserves previous output after a model error.
| Definition type | C type |
|---|---|
| Boolean | bool |
| Integer | int64_t |
| Number | double |
| String | stardust_bytes |
| Array | Typed pointer, length and is_null flag |
| Object without a schema | sd_sdk_map |
| Entity | int64_t, encoded as a native link |
| Native timestamp or signed duration | struct timespec |
| Unknown or other non-object schema reference | sd_sdk_value |
Object schema references use typed pointers. Arrays of schema records use typed slices. Nested arrays retain each list level. Dynamic values preserve native integer and floating-point variants, tagged object shapes and explicit null. Strings and keys have explicit byte lengths, including embedded NUL bytes.
Optional schema fields, defaulted parameters and clause filters have a present
flag and a typed value. False presence omits the field. True presence supplies
false, zero or empty values. A list's is_null flag selects null, independently
of its length. A zero-length list with false is_null supplies an empty array.
A present null schema pointer supplies null. Declared parameters supplied with null
report missing_parameter. Predicate-only inputs remain required fields.
Aggregate and window outputs are not filter inputs.
Ownership and limits
Input structs borrow caller storage during a call. Each decoded schema value,
expression result and query row has its own sd_sdk_owner.
Close each owner with sd_sdk_owner_close. A row stays valid after cursor close.
Keep the allocator callbacks and their context valid until all owners close.
Do not close a copied owner as if it owned separate storage.
Queries own their native query, rows, page and metadata table. Exhaustion and the first error release those resources immediately. Close a cursor after an early exit with its generated close function. The cursor owns its diagnostic until close. Keep the database open while advancing the cursor. Repeated close is safe.
Mutation functions commit their patch and return an sd_sdk_commit. Close it
with sd_sdk_commit_close. Expression functions use the database's current macros
and release native program, result and document handles after decoding.
sd_sdk_error owns its diagnostic bytes. Close it with sd_sdk_error_close.
Conversion errors include field paths, with quoted keys when needed.
An error-copy allocation failure returns a static out_of_memory diagnostic.
Initialize output owners, errors and cursors to zero. Close an existing output
before reusing its storage. Null required output pointers return STARDUST_INVALID_ARGUMENT.
Give an owner or cursor exclusive access during a call or close.
A zero sd_sdk_config selects malloc/free, 64 MiB per host owner and one million
native nodes per conversion. Custom allocator callbacks must return memory aligned
for max_align_t. Byte and node capacities can be set before conversion.
Metadata tables, each returned graph and each diagnostic have separate owners.
Their combined memory can exceed one owner's byte capacity.
Native handles use additional engine memory governed by the ABI and database options. The fixed depth limit is 128 and the field-path limit is 4096 bytes. Cycles, excessive depth, allocation failures and size overflow return errors.
Use SD_SDK_TEXT_LITERAL only with string literals. Construct runtime strings
with an explicit stardust_bytes pointer and length.
Cleanup does not allocate memory. Conversion reads native node tables and writes
native builders directly, without JSON or text round trips.
For these stored definitions:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 1C 2A | txt "*" |
| 06 | 2C 3F6E | txt "?n" |
| 09 | 02 02000000000000 00 | i64 2 |
| 12 | 0E 15 00 0700 030000 00 04000000 060000 00 09000000 | array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @4 → @6 → @9 |
| 27 | 5C 3F6974656D | txt "?item" |
| 2D | 0E 0D 00 0100 010000 00 27000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @27 |
| 3A | 5C 3F6974656D | txt "?item" |
| 40 | 3C 6B6579 | txt "key" |
| 44 | 4C 3F6B6579 | txt "?key" |
| 49 | 0E 15 00 0700 030000 00 3A000000 400000 00 44000000 | array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @3A → @40 → @44 |
| 5E | 0E 0D 00 0100 010000 00 49000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @49 |
| 6B | 5C 7768657265 | txt "where" |
| 71 | 0F 0A 6B000000 5E00 0000 | map leaf, size 10: @6B → @5E |
| 7B | 4C 66696E64 | txt "find" |
| 80 | 0F 0A 7B000000 2D00 0000 | map leaf, size 10: @7B → @2D |
| 8A | 07 0E 0A000000 7100 0000 80000000 | map branch, size 14, depth 0 (root), slots 1, 3: @71 → @80 |
| 98 | DC 71756572696573 2F6974656D73 | txt "queries/items" |
| A6 | 0F 0A 98000000 8A00 0000 | map leaf, size 10: @98 → @8A |
| B0 | 14 12 657870726573 73696F6E732F646F 75626C65 | txt "expressions/double" |
| C4 | 0F 0A B0000000 1200 0000 | map leaf, size 10: @B0 → @12 |
| CE | 07 0E 08010000 A600 0000 C4000000 | map branch, size 14, depth 0 (root), slots 3, 8: @A6 → @C4 |
| DC | CE000000 00000000 | root → @CE; previous: none |
#include "expressions.h"
#include "queries.h"
#include <stdio.h>
uint32_t sdk_example(stardust_db *database) {
sd_sdk_config config = {0};
sd_sdk_error error = {0};
sd_sdk_owner owner = {0};
sd_query_items_cursor cursor = {0};
sd_query_items_row row = {0};
sd_expression_double_parameters input = {0};
sd_query_items_parameters filters = {0};
int64_t doubled = 0;
bool has_row = false;
input.n = 21;
uint32_t status = sd_expression_double_(database, &input, config,
&doubled, &owner, &error);
if (status != STARDUST_OK) goto done;
printf("%lld\n", (long long)doubled);
sd_sdk_owner_close(&owner);
filters.key.present = true;
filters.key.value.kind = SD_SDK_STRING;
filters.key.value.as.text = SD_SDK_TEXT_LITERAL("b");
status = sd_query_items(database, &filters, NULL, config, &cursor, &error);
if (status != STARDUST_OK) goto done;
while (true) {
status = sd_query_items_next(&cursor, &row, &owner, &has_row);
if (status != STARDUST_OK || !has_row) break;
printf("%lld\n", (long long)row.item);
sd_sdk_owner_close(&owner);
}
done:
sd_sdk_owner_close(&owner);
sd_query_items_close(&cursor);
sd_sdk_error_close(&error);
return status;
}File ownership
The plugin owns the entire configured output directory. Every successful render removes its previous contents, including edited files, unrelated files and subdirectories.
Keep application source, database files and other data outside this directory.
Do not edit generated files: the next run replaces your changes.
The plugin retains only its internal .stardust-sdk.lock file between runs.
The plugin renders and validates the package before it removes any files. Rendering errors leave the previous directory contents unchanged. A filesystem error during cleanup or writing can leave the directory empty or partly written. The next successful run clears that partial output and writes a fresh package. Use sync before a build and check its result.
Each file is written through an atomic rename. Rebuilding the whole directory is not atomic.
A destination lock prevents concurrent publication and reports Destination_Busy when another publisher holds it.
Use a separate output directory for each database, with no directory nested inside another output directory.
Cleanup removes symlinks inside the output directory without following them.
The output directory itself and the lock file must not be symlinks.
Directory inspection is limited to 65,536 entries, 64 levels and 16 MiB of stored paths.
An inspection failure leaves the previous contents unchanged.
Operations
| Operation | Returns | Summary |
|---|---|---|
| status | Unavailable | sdk.status() returns the SDK generator's owned status document. |
| sync | Unavailable | sdk.sync(options) waits until the SDK generator published the definitions committed before the call. |
status
C calls plugin operations through stardust_db_plugin_call and stardust_db_plugin_stream_open with a request document.
Also calls: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release
sync
C calls plugin operations through stardust_db_plugin_call and stardust_db_plugin_stream_open with a request document.
Also calls: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release