GreyCat C API and GCL Standard Library reference. Use for: (1) Native C development with gc_machine_t context, tensors, objects, memory management, crypto, I/O; (2) GCL Standard Library modules - std::core (Date/Time/Tuple/geospatial types), std::runtime (Scheduler/Task/Logger/User/Security/System/OpenAPI/MCP), std::io (CSV/JSON/XML/HTTP/Email/FileWalker), std::util (Queue/Stack/SlidingWindow/Gaussian/Histogram/Quantizers/Random/Plot); (3) Plugin development patterns - lifecycle hooks, type configuration, nativegen, module-level and type-level function linking, global state, thread safety, conditional logging. Keywords: GreyCat, GCL, native functions, tensors, task automation, scheduler, plugin development.
Comprehensive reference for GreyCat native development (C API), the GCL Standard Library, and plugin development patterns. Tracks SDK 8.3 (headers re-verified 2026-09-09 against upstream 0dc6e7632). Changes since 8.2:
gc_object__is_instance_of now accounts for monomorphization — a Box<String> instance now tests true against the generic declaration Box, not just exact-type/inheritance matches as before. Signature unchanged. See api_memory_text.md.runtime::Log.time is explicitly @format(DurationUnit::microseconds) — the field was always written as raw epoch microseconds by the log writer, now the type declares it instead of leaving it implicit. Reading files/root/log.csv with CsvReader<runtime::Log> needs CsvFormat { string_delimiter: '\0' }, since the payload column is written unescaped. See standard_library.md.Previously, in 8.2 (headers re-verified 2026-08-28 against upstream e1e7edf54):
gc_dtz_time__parse_format(str, len, format, format_len, tz, out_epoch_us) — parses a date/time string against an explicit format, the counterpart of gc_dtz_time__print and the custom-format arm of gc_dtz_time__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 api_services.md.gc_program_library. Two optional gc_hook_function_t * fields — install and codegen — with setters gc_program_library__set_install_hook(lib, hook) and gc_program_library__set_codegen_hook(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 plugin_development.md, api_core.md.gc/env.h defines gc_env_slot_t (tagged-by-convention union: bool/i64_t/u64_t/f64_t/char *) and gc_env_options_offset_t (one variant per CLI/.env-resolved option, e.g. gc_env_options__port, gc_env_options__ca_path, terminated by the length marker gc_env_options_len). The new gc_host__options(host) returns a const gc_env_slot_t * array indexed by that enum, valid for the life of the host. gc/ca.h's new gc_ssl_ca__pem_bundle(u64_t *len) hands out the resolved TLS trust chain (system CA store + ca_path) 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: api_runtime_storage.md.gc_machine__this_type(self) — the gc_type_t counterpart to gc_machine__this; returns gc_type_undefined (and must not be called) when the current frame has no receiver. New: gc_abi__finalize_ex(abi) — releases everything a load allocated but not abi itself, for callers that own a gc_abi_t inline rather than through gc_abi__create. Both added for the Rust SDK rewrite. See api_core.md, api_runtime_storage.md.gc_slot_t union field order changed. u64_t 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 gc_slot_t initializer ((gc_slot_t){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 api_core.md.gc/buffer.h type-name helpers removed, no replacement. gc_buffer__add_type_name, gc_buffer__add_type_name_by_id, and gc_buffer__add_type_qname 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 gc_sdk-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 (gc_buffer__write_u8 / _bool / _u16 / _u32 / _u64 / _u64_at / _f64 / _vu32 / _vu64 / _vi64 / _ptr), now reliably linkable from callers (e.g. FFI bindings) that can't call a C static inline function. See api_memory_text.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 (gc_buffer_read_bool / _u8 / _i8 / _u16 / _u32 / _i32 / _u64 / _i64 / _f32 / _f64 / _vu32 / _vu64 / _vi64) was removed, replaced by bool-returning _size_checked equivalents; new gc_buffer_read_ptr_size_checked and GC_VU32_MAX_BYTES; gc_buffer_unavailable 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 gc_table__init (param renamed table→self) now leaves the table untouched on allocation failure — callers must check self->capacity. Full detail: api_memory_text.md, api_collections.md.
gc_allocator_t *: per-call scratch from gc_machine__allocator(ctx) (= ((gc_ctx_t *)ctx)->allocator), plugin-global state from gc_host__global_allocator(). gc_alloc__create(bool shared) (true = multi-thread arena). gc_alloc__free(a, ptr, size) requires the original size. The thread-bound gc_malloc / gc_free / gc_realloc helpers target whatever gc_alloc__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: api_memory_text.md.gc/log.h). gc_log_level_t is none / error / warn / info / perf / trace. Use gc_log__machine / gc_log__machinef (VM context) and gc_log__host / gc_log__hostf (host context); gate hot paths with gc_log__enabled(host, level).gc/str.h: gc_str_t and the gc_core_str / gc_core_t2…t4f globals are not public. Use gc_string_t (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__call_function takes a const gc_program_function_t *fn (not a raw body pointer). On false the result is a synthesized Error object (type gc_core_Error) and *marked_res_type is gc_type_object; the caller owns one mark on the result. gc_machine__impersonate(ctx, user_id) switches the effective user for permission-aware sub-calls.gc/host.h). gc_host__cancel_task(self, task_id, requester_id, requester_permissions, out_task) is now thread-safe and permission-checked (out_task optional, receives a copy of the cancelled task); gc_host__get_task_status still takes just i64_t task_id. gc_host__spawn_task takes a u64_t user_permissions mask. Periodic scheduling via gc_scheduler_t, gc_periodic_task_t, and gc_periodicity_t (a struct holding a gc_periodicity_type_t type: fixed / daily / weekly / monthly / yearly). New in 8.2: gc_host__options(self) (resolved CLI/env/.env config, see gc/env.h) and gc/ca.h's gc_ssl_ca__pem_bundle(len) (resolved TLS trust chain). See api_runtime_storage.md.GC_ABI_PROTO is 3. gc_abi_header_check_error_t includes ..._truncated = 4; gc_abi_t carries its own allocator. New in 8.2: gc_abi__finalize_ex(abi) for inline-owned gc_abi_t instances (frees the load's allocations, not abi itself).gc_block_t gained u64_t node_ref (new in 8.1) — the node reference the block backs, used by suspend/resume serialization to relocate the block's entries. See api_runtime_storage.md.gc_program_iterator_param_t: from=0, to=1, nullable=2, from_excl=3, to_excl=4 (no limit). Geo epsilon constant is GC_CORE_GEO_EPS.gc_tensor_t / gc_tensor_descriptor_t (formerly gc_core_tensor_t / gc_core_tensor_descriptor_t); the gc_core_tensor__* and gc_core_tensor_descriptor__* function names are unchanged, and gc_machine__init_tensor now takes/returns the renamed types. Plugin code that referenced the old struct typedefs must be updated. Full tensor API: api_collections.md.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): standard_library.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 speed_smoothed (EMA of the per-update pace) and smoothing (EMA weight, default ProgressTracker.DEFAULT_SMOOTHING = 0.1) drive a more reactive remaining estimate. Also new since the last sync: HttpRequest.max_response_size (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.gc_common__parse_number's str_len param widened u32_t * → u64_t * (its sibling gc_common__parse_sign_number is still u32_t * — the two now disagree, match the local variable's type to the callee). gc_buffer_read_vu64_size_checked added (the u64_t counterpart of the existing _vu32_ variant), joined later in 8.1 by gc_buffer_read_vi64_size_checked (zig-zag signed) and the GC_VU64_MAX_BYTES (= 9) worst-case varint width constant. gc_object__clone, gc_array__fill, and gc_array__ensure_capacity (replaced gc_array__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>>.gc_machine_t - Execution context passed to all native functions. Use to get parameters, set results, report errors, create objects, and access scratch buffers.
gc_slot_t - Universal value container (tagged union) holding any GreyCat value: integers, floats, bools, objects, enums, tuples, etc.
gc_type_t - Type system enum (8-bit, 24 values) defining all GreyCat types: null, bool, char, int, float, node variants, geo, time, duration, cubic, static_field, object, block_ref, block_inline, function, undefined, type, field, stringlit, error.
gc_object_t - 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.
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 gc_type_int; they are gc_type_static_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__set_runtime_error(ctx, "msg") / gc_machine__set_runtime_error_syserr(ctx) (uses errno); check propagated errors with if (gc_machine__error(ctx)) return;.
The C API reference is split by domain — each file below is linked directly (one level deep) and loads on demand.
references/api_core.md — value model, execution context, type system, logging. Start here.
gc_machine_t (params, result, errors, gc_machine__allocator, gc_machine__impersonate, gc_machine__call_function via gc_program_function_t *, object creation)gc_type_t / gc_slot_t value model, complex c64/c128 arithmetic, gc_node__parsegc_program__link_mod_fn / gc_program__link_type_fn), type configuration, introspection, iterator params, DurationUnitgc/log.h) and the cross-cutting Conventions & Patterns indexreferences/api_memory_text.md — memory, buffers, strings, objects
gc_alloc__create(bool shared), sizing/statsgc_string_t, allocator-aware constructors)references/api_collections.md — array, map, table, tensor
init_Nd, get/set/add for i32/i64/f32/f64/c64/c128, descriptor utilities, raw data access, matmul/bias/sumreferences/api_runtime_storage.md — runtime, persistence, graph nodes
gc_scheduler_t, gc_periodic_task_t), plugin-global allocator (gc_host__allocator / gc_host__global_allocator)gc_node__resolve, gc_node__parse) and direct node-entry read/write (gc_machine_native__node_get / node_set_at via gc_node_single_value_t, released with gc_node_single_value__clear) — this u64_t node_ref API is uniform across all node variants (node, nodeTime, nodeList, nodeGeo, nodeIndex); only the key encoding differs per variant. Breaking in 8.1: node_get now takes an expected_type_id (gc_core_nodeTime / nodeList / nodeGeo / nodeIndex) before ctx and returns a null single plus a ctx runtime error if the resolved block is a different typereferences/api_services.md — crypto, geo, time, math, util
gc_dtz_time__print / parse)File: references/standard_library.md
Load when working with:
Contains: Complete documentation for all four standard library modules with code examples, usage patterns, and best practices.
Build native GreyCat plugins in C with proper lifecycle management, type configuration, and thread safety.
Function naming (CRITICAL — must match nativegen): gc_<module>_<Type>__<methodName>(gc_machine_t *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:
gc_<gcl_module>_<GclType>__<methodName> (double underscore before the method)gc_<gcl_module>__<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 -> lib_start -> [worker_start -> native calls -> worker_stop] -> lib_stop
Type configuration: gc_program_type__configure(prog, type_id, sizeof(my_struct_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>`
File: references/plugin_development.md
Load when:
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__is_instance_of), result returning, error handling, conditional logging, and a full end-to-end plugin example.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer