GreyCat SDK - C API, Standard Library & Plugin Development
Comprehensive reference for GreyCat native development (C API), the GCL Standard Library, and plugin development patterns. Tracks SDK 8.2 (headers re-verified 2026-08-28 against upstream e1e7edf54). Changes since 8.1:
- New:
gcdtztime__parseformat(str, len, format, formatlen, tz, outepochus) — parses a date/time string against an explicit format, the counterpart of gcdtztime__print and the custom-format arm of gcdtztime__parse. A format leaves the instant naive, so tz is the zone it is read in; returns false when the input does not match the format, or names an instant the zone does not have. See [apiservices.md](references/apiservices.md).
- New: CLI hooks on
gcprogramlibrary. Two optional gchookfunctiont * fields — install and codegen — with setters gcprogram_library__setinstallhook(lib, hook) and gcprogramlibrary__setcodegenhook(lib, hook). The install hook runs at the end of a successful greycat install (after the project is rebuilt and linked) for every library that registers one; returning false fails the command. The codegen hook makes the library itself a generator for greycat codegen <lang>, dispatched by matching <lang> against library names (the built-in c/ts/java/rust/python2 generators stay native to the CLI). Both are purely additive — a NULL hook opts out. See [plugindevelopment.md](references/plugindevelopment.md), [apicore.md](references/apicore.md).
- New: resolved host configuration exposed to native libraries.
gc/env.h defines gcenvslott (tagged-by-convention union: bool/i64t/u64t/f64t/char ) and gcenvoptionsoffsett (one variant per CLI/.env-resolved option, e.g. gcenvoptions__port, gcenvoptions__capath, terminated by the length marker gcenvoptionslen). The new gc_host__options(host) returns a const gcenvslott array indexed by that enum, valid for the life of the host. gc/ca.h's new gcssl_ca__pembundle(u64t *len) hands out the resolved TLS trust chain (system CA store + capath) as a PEM byte string, owned by the runtime — for libraries that verify their own TLS connections and want to honor the same trust config as the host. Both headers are pulled in automatically via greycat.h. Full detail: [apiruntimestorage.md](references/apiruntime_storage.md).
- New:
gc_machine__thistype(self) — the gctypet counterpart to gcmachine__this; returns gctypeundefined (and must not be called) when the current frame has no receiver. New: gc_abi__finalizeex(abi) — releases everything a load allocated but not abi itself, for callers that own a gcabit inline rather than through gcabi__create. Both added for the Rust SDK rewrite. See [apicore.md](references/apicore.md), [apiruntimestorage.md](references/apiruntimestorage.md).
- Breaking:
gcslott union field order changed. u64t u64 is now the first member (previously bool b was first), to stop positional (non-designated) initializers from silently truncating through bool. Any code using a non-designated gcslott initializer ((gcslott){x} with no .field =) now targets .u64 instead of .b — designated initializers ({.i64 = x}, {.object = p}, etc., used throughout this SDK's own examples) are unaffected. See [apicore.md](references/api_core.md).
- Breaking: three
gc/buffer.h type-name helpers removed, no replacement. gc_buffer__addtypename, gc_buffer__addtypenamebyid, and gc_buffer__addtypeqname are gone from the header entirely (briefly exported, then removed again in the same cycle — never shipped as stable). Code calling any of the three no longer compiles. Every remaining gc/buffer.h function is now uniformly gcsdk-exported (some previously lacked the export macro and could fail to link from a plugin shared library on strict-visibility builds) — this includes the pre-existing non-static inline writer functions (gcbuffer__writeu8 / bool / u16 / u32 / u64 / u64at / f64 / vu32 / vu64 / vi64 / ptr), now reliably linkable from callers (e.g. FFI bindings) that can't call a C static inline function. See [apimemorytext.md](references/apimemorytext.md).
Previously, in 8.1 (headers re-verified 2026-08-04 against upstream 78e57676d): gc/buffer.h's read side was overhauled — every unchecked inline reader (gcbufferreadbool / u8 / i8 / u16 / u32 / i32 / u64 / i64 / f32 / f64 / vu32 / vu64 / vi64) was removed, replaced by bool-returning sizechecked equivalents; new gcbufferreadptrsizechecked and GCVU32MAXBYTES; gcbufferunavailable params are now const-qualified and it no longer special-cases NULL/underflowed cursors (breaking for any code relying on the removed void-returning readers or that fail-closed guard). gc/table.h's gctable__init (param renamed table→self) now leaves the table untouched on allocation failure — callers must check self->capacity. Full detail: [apimemorytext.md](references/apimemorytext.md), [apicollections.md](references/apicollections.md).
Key Considerations
- Allocator API is mandatory. Every non-trivial allocation routes through an explicit
gcallocatort : per-call scratch from gc_machine__allocator(ctx) (= ((gcctxt )ctx)->allocator), plugin-global state from gc_host__globalallocator(). gcalloc__create(bool shared) (true = multi-thread arena). gc_alloc__free(a, ptr, size) requires the original size. The thread-bound gcmalloc / gcfree / gcrealloc helpers target whatever gcalloc__bind set. New in 8.1: gc_alloc__reset(allocator) — destructively resets a whole arena (reclaims even leaked/lost pointers on the jemalloc path), invalidating every prior pointer from that allocator; use between iterations of a long-lived worker loop after tearing everything down, no-op on native-malloc/WASM/standalone. Sizing/stats and full patterns: [apimemorytext.md](references/apimemorytext.md).
- Structured logging (
gc/log.h). gcloglevelt is none / error / warn / info / perf / trace. Use gclog__machine / gc_log__machinef (VM context) and gc_log__host / gc_log__hostf (host context); gate hot paths with gc_log__enabled(host, level).
- No inline short strings. There is no
gc/str.h: gcstrt and the gccorestr / gccoret2…t4f globals are not public. Use gcstringt (heap, immutable, hash-cached; buffer IS NUL-terminated at buffer[size] — but size is still the authoritative content length, since the content itself may embed NUL bytes).
- **
gc_machine__callfunction takes a const gcprogramfunctiont *fn** (not a raw body pointer). On false the result is a synthesized Error object (type gccoreError) and *markedrestype is gctypeobject; the caller owns one mark on the result. gc_machine__impersonate(ctx, user_id) switches the effective user for permission-aware sub-calls.
- Host/Scheduler (
gc/host.h). gc_host__canceltask(self, taskid, requesterid, requesterpermissions, outtask) is now thread-safe and permission-checked (outtask optional, receives a copy of the cancelled task); gc_host__gettaskstatus still takes just i64t taskid. gc_host__spawntask takes a u64t userpermissions mask. Periodic scheduling via gcschedulert, gcperiodictaskt, and gcperiodicityt (a struct holding a gcperiodicitytypet type: fixed / daily / weekly / monthly / yearly). New in 8.2: gchost__options(self) (resolved CLI/env/.env config, see gc/env.h) and gc/ca.h's gcsslca__pembundle(len) (resolved TLS trust chain). See [apiruntimestorage.md](references/apiruntime_storage.md).
- ABI.
GCABIPROTO is 3. gcabiheadercheckerrort includes ...truncated = 4; gcabit carries its own allocator. New in 8.2: gc_abi__finalizeex(abi) for inline-owned gcabi_t instances (frees the load's allocations, not abi itself).
gcblockt gained u64t noderef (new in 8.1) — the node reference the block backs, used by suspend/resume serialization to relocate the block's entries. See [apiruntimestorage.md](references/apiruntimestorage.md).
- Iterator params
gcprogramiteratorparamt: from=0, to=1, nullable=2, fromexcl=3, toexcl=4 (no limit). Geo epsilon constant is GCCOREGEO_EPS.
- Tensor struct rename (breaking). The tensor structs are now
gctensort / gctensordescriptort (formerly gccoretensort / gccoretensordescriptort); the gccoretensor__ and gccoretensor_descriptor__ function names are unchanged, and gc_machine__inittensor now takes/returns the renamed types. Plugin code that referenced the old struct typedefs must be updated. Full tensor API: [apicollections.md](references/api_collections.md).
- Stdlib (GreyCat 8.0) breaking changes. Security model is
Identity / IdentityGrant / IdentityGrantType (the old User / UserGroup / SecurityPolicy / OpenIDConnect types are gone). GCL logging is via module-level info / warn / error / perf / trace functions — Log is a parse record, not a callable namespace. Remaining 8.0 surface (S3 object storage, the HttpMethod/HttpRequest/HttpResponse model (HttpRequest.headers is Map<String, String>?, HttpResponse.headers is Map<String, String>), Csv::analyze(Array<String>), Uuid v4/v7, periodicity field shapes, LogLevel/TaskStatus/LicenseType enums): [standardlibrary.md](references/standardlibrary.md).
ProgressTracker.update(nb) is now absolute, not incremental (breaking). It sets the step counter to nb rather than adding nb to it. New fields speedsmoothed (EMA of the per-update pace) and smoothing (EMA weight, default ProgressTracker.DEFAULTSMOOTHING = 0.1) drive a more reactive remaining estimate. Also new since the last sync: HttpRequest.maxresponsesize (caps chunked/unbounded response reads) and Task::live(ids) / Task::tasks(ids) (bulk liveness check / bulk fetch by id).
TensorDistance gained lorentz and poincare (hyperbolic distances — Lorentz/hyperboloid model and Poincaré ball model, both curvature fixed at -1). Identity.set_role(name, role) is a new admin-only static native — it returns nothing (void), not bool.
- Other 8.0→8.1 signature/field changes.
gc_common__parsenumber's strlen param widened u32t → u64t (its sibling gc_common__parsesignnumber is still u32t * — the two now disagree, match the local variable's type to the callee). gcbufferreadvu64sizechecked added (the u64t counterpart of the existing vu32 variant), joined later in 8.1 by gcbufferreadvi64sizechecked (zig-zag signed) and the GCVU64MAXBYTES (= 9) worst-case varint width constant. gcobject__clone, gc_array__fill, and gc_array__ensurecapacity (replaced gcarray__init; now grow-only/idempotent, rounds to a power of two, preserves contents) round out the collections/memory surface. Stdlib: Task.duration: duration? was replaced by Task.completion: time?, Task gained user_name: String, and nodeGeo<T> gained search(center: geo, max: int): Array<SearchResult<geo,T>>.
Contents
- C API - Native function implementation, tensor operations, object manipulation, maps, arrays, tables, geospatial, time/date, crypto, buffers, I/O
- Standard Library (std) - GCL runtime features, I/O, collections, and utilities
- Plugin Development - Complete guide to building native plugins with lifecycle hooks, type configuration, and real-world patterns
GreyCat C API
Core Concepts
gcmachinet - Execution context passed to all native functions. Use to get parameters, set results, report errors, create objects, and access scratch buffers.
gcslott - Universal value container (tagged union) holding any GreyCat value: integers, floats, bools, objects, enums, tuples, etc.
gctypet - Type system enum (8-bit, 24 values) defining all GreyCat types: null, bool, char, int, float, node variants, geo, time, duration, cubic, staticfield, object, blockref, block_inline, function, undefined, type, field, stringlit, error.
gcobjectt - Generic handle for heap-allocated objects. Packed to 128 bits. Every collection type (Array, Map, Table, Tensor, String, Buffer) starts with this as its first member.
Core Operations
The few patterns below are the trigger-level essentials. Full runnable examples for every operation (objects, tensors, arrays, maps, strings, buffers, allocators, logging, introspection) live in the five C-API reference files listed under Detailed Reference.
Parameters & results:
gc_slot_t p = gc_machine__get_param(ctx, 0); gc_type_t t = gc_machine__get_param_type(ctx, 0);
u32_t n = gc_machine__get_param_nb(ctx); gc_slot_t self = gc_machine__this(ctx); // instance methods
gc_machine__set_result(ctx, (gc_slot_t){.i64 = 42}, gc_type_int);
gc_machine__set_result(ctx, (gc_slot_t){.object = obj}, gc_type_object);
gc_object__un_mark(obj, ctx); // CRITICAL: every object result must be un-marked or it leaks / GC-faults
Enum parameters & results (CRITICAL — #1 native bug). GCL enum values are NOT gctypeint; they are gctypestatic_field with the ordinal in .tu32.right (.tu32.left is the enum type offset), never .i64:
// WRONG — always hits the default fallback: (type == gc_type_int) ? slot.i64 : 0
// CORRECT:
i64_t variant = (gc_machine__get_param_type(ctx, 0) == gc_type_static_field) ? (i64_t) slot.tu32.right : 0;
// Returning MyEnum::variant2 (ordinal 1):
gc_machine__set_result(ctx, (gc_slot_t){.tu32 = {.left = 0, .right = 1}}, gc_type_static_field);
Errors: gc_machine__setruntimeerror(ctx, "msg") / gc_machine__setruntimeerrorsyserr(ctx) (uses errno); check propagated errors with if (gcmachine__error(ctx)) return;.
Detailed Reference
The C API reference is split by domain — each file below is linked directly (one level deep) and loads on demand.
[references/apicore.md](references/apicore.md) — value model, execution context, type system, logging. Start here.
- Native C functions with
gcmachinet (params, result, errors, gc_machine__allocator, gc_machine__impersonate, gc_machine__callfunction via gcprogramfunctiont *, object creation)
gctypet / gcslott value model, complex c64/c128 arithmetic, gc_node__parse
- Program/Type system: linking (
gc_program__linkmodfn / gc_program__linktypefn), type configuration, introspection, iterator params, DurationUnit
- Structured logging (
gc/log.h) and the cross-cutting Conventions & Patterns index
[references/apimemorytext.md](references/apimemorytext.md) — memory, buffers, strings, objects
- Memory allocation: per-call, plugin-global, aligned, thread-bound helpers,
gc_alloc__create(bool shared), sizing/stats
- Buffer building and binary read/write (varint, zig-zag encoding)
- Heap-allocated immutable strings (
gcstringt, allocator-aware constructors)
- Object/field manipulation, type introspection, GC mark/un-mark, serialization
[references/apicollections.md](references/apicollections.md) — array, map, table, tensor
- Array, Map, Table get/set/add operations
- Tensors:
init_Nd, get/set/add for i32/i64/f32/f64/c64/c128, descriptor utilities, raw data access, matmul/bias/sum
[references/apiruntimestorage.md](references/apiruntimestorage.md) — runtime, persistence, graph nodes
- Host/Task management (spawn, cancel, status), periodic scheduler (
gcschedulert, gcperiodictaskt), plugin-global allocator (gchost__allocator / gc_host__global_allocator)
- Storage blocks (attach/detach objects), ABI (allocator-aware, proto=3, truncated-header error), file I/O (open/sync)
- Node resolution (
gc_node__resolve, gc_node__parse) and direct node-entry read/write (gcmachinenative__nodeget / nodesetat via gcnodesinglevaluet, released with gcnodesinglevalue__clear) — this u64t noderef API is uniform across all node variants (node, nodeTime, nodeList, nodeGeo, nodeIndex); only the key encoding differs per variant. Breaking in 8.1: nodeget now takes an expectedtypeid (gccore_nodeTime / nodeList / nodeGeo / nodeIndex) before ctx and returns a null single plus a ctx runtime error if the resolved block is a different type
[references/apiservices.md](references/apiservices.md) — crypto, geo, time, math, util
- Cryptography (SHA-256, HMAC-SHA-256, Base64, Base64URL)
- Geospatial (geohashing, Haversine distance); Time/Date/Timezone (formatting with
gcdtztime__print / parse)
- Math (WASM shims, f64 PI/TAU/E/LN2 constants); Utility (Morton codes, hex, parsing, deep equality, sorting with allocator, licensing)
GreyCat Standard Library (std)
Module Organization
- std::core - Fundamental types (Date, Time, Duration, Tuple, Error, geospatial types, enumerations)
- std::runtime - Scheduler, Task, Job, Logger, Identity/Security, System, ChildProcess, License, OpenAPI, MCP
- std::io - Text/Binary I/O, CSV, JSON, XML, HTTP client, Email/SMTP, FileWalker, S3 object storage
- std::util - Collections (Queue, Stack, SlidingWindow, TimeWindow), Statistics (Gaussian, Histogram, GaussianProfile), Quantizers (Linear/Log/Custom/Multi), Assert, ProgressTracker, Crypto, Uuid, Random
Detailed Reference
File: [references/standardlibrary.md](references/standardlibrary.md)
Load when working with:
- Task scheduling and automation (Scheduler with periodicities)
- File I/O operations (CSV, JSON, XML, binary files)
- HTTP integration and REST APIs, S3 object storage
- Statistical analysis and data processing
- Identity, security, and authentication
- System operations and logging
Contains: Complete documentation for all four standard library modules with code examples, usage patterns, and best practices.
Plugin Development Guide
Overview
Build native GreyCat plugins in C with proper lifecycle management, type configuration, and thread safety.
Key Patterns
Function naming (CRITICAL — must match nativegen): gc<module><Type>__<methodName>(gcmachinet *ctx)
When GreyCat compiles GCL code with native declarations, it auto-generates nativegen.c / nativegen.h. Those extern declarations define the exact C symbol names the runtime resolves at dlopen — your C definitions MUST match or you get undefined symbol errors. Convention:
- Type method:
gc<gclmodule>_<GclType>__<methodName> (double underscore before the method)
- Module function:
gc<gclmodule>__<functionName> (double underscore before the function)
<gcl_module> is the GCL file's module path; <GclType> matches the GCL type (PascalCase); <methodName> matches the GCL method (camelCase)
// GCL (module "text_normalizer", type TextNormalizer):
// native static fn rejoinHyphenatedWords(text: String): String;
// nativegen.h generates:
// extern void gc_text_normalizer_TextNormalizer__rejoinHyphenatedWords(gc_machine_t *ctx);
// Your C implementation MUST be named exactly:
void gc_text_normalizer_TextNormalizer__rejoinHyphenatedWords(gc_machine_t *ctx) { ... }
Plugin lifecycle: link -> libstart -> [workerstart -> native calls -> workerstop] -> libstop
Type configuration: gcprogramtype__configure(prog, typeid, sizeof(mystruct_t), finalizer)
Library hooks:
gc_program_library__set_lib_hooks(lib, lib_start, lib_stop);
gc_program_library__set_worker_hooks(lib, worker_start, worker_stop);
gc_program_library__set_install_hook(lib, lib_install); // optional: end of `greycat install`
gc_program_library__set_codegen_hook(lib, lib_codegen); // optional: `greycat codegen <lang>`
Detailed Reference
File: [references/plugindevelopment.md](references/plugindevelopment.md)
Load when:
- Building a new native plugin from scratch
- Setting up CMake build configuration for .gclib output
- Implementing nativegen.c/h (symbol resolution, type/function linking)
- Linking module-level native functions (gc_program__linkmodfn) or type methods (gc_program__linktypefn)
- Managing library/worker lifecycle hooks
- Wrapping C library handles in GreyCat objects (boxing pattern)
- Implementing thread-safe global state with mutexes
- Mapping GCL enums to C library enums
- Using the buffer reuse and tokenization retry patterns
- Implementing conditional logging (gc_machine__loglevel / gclog__enabled)
Contains: Complete project structure, CMake configuration, GCL type definitions, nativegen implementation, lifecycle hooks, custom type configuration with finalizers, global state management, memory management patterns, parameter handling (including type checking with gc_object__isinstanceof), result returning, error handling, conditional logging, and a full end-to-end plugin example.