8.4.633-stable Switch to dev

GreyCat Server

Introduction

To empower digital twin, GreyCat is not only a Runtime but also a Server.

Any function can be exposed to remote procedure call (RPC) by adding an @expose pragma

@expose
fn compute(p: int): int {
    return 3 + 5 + p;
}
fn main() {
    println(compute(2));
}

This minimal code snippet already defines a server (–user=root impersonates the root user and bypasses authentication, more on this later)

greycat serve --user=root
# 10
# INFO  2026-10-27T09:28:06.293689Z GreyCat is serving on port: 8080, runtime: 8.4.594-stable+fe2b2a3fc
# WARN  2026-10-27T09:28:06.293715Z Impersonate mode with user: root

GreyCat server can be queried through an HTTP API.

The parameters for the endpoint can be url encoded:

curl -X POST -d "[100]" http://localhost:8080/project::compute
> 108

Or the parameters for the endpoint can be JSON encoded:

curl -X POST --json "[100]" http://localhost:8080/project::compute
> 108

From a terminal, greycat call does the same without writing the request, and authenticates with the root token of the project (see Call):

greycat call project::compute 100
> 108

Functions and error handling

fn sub_process() {
    throw "stop here";
}
@expose
fn computeWithSub(p: int): int {
    sub_process();
    return 3 + 5 + p;
}
fn main() {
    var res = computeWithSub(2);
    println(res);
}

In this example, sub_process and computeWithSub share error handling management, leading to the following error stack

greycat run --format=json
{
  "message": "stop here",
  "stack": [
    { "module": "project", "function": "main", "line": 10, "column": 33 },
    { "module": "project", "function": "computeWithSub", "line": 6, "column": 18 },
    { "module": "project", "function": "sub_process", "line": 2, "column": 23 }
  ]
}

Function entrypoint == Transaction

Every exception thrown activates a rollback mechanism, leaving the temporal graph unchanged

var counter: node<int?>;

@expose
fn inc_count() {
    println(*counter);
    counter.set((counter.resolve() ?? 0) + 1);
    throw "my bad";
}

This is illustrated in this example, where multiple calls do not alter the counter due to the following exception

greycat run inc_count --format=json
# null
# {"message":"my bad","stack":[{"module":"project","function":"inc_count","line":7,"column":20}]}
greycat run inc_count --format=json
# null
# {"message":"my bad","stack":[{"module":"project","function":"inc_count","line":7,"column":20}]}

Exposed functions are for short computations!

Like many API-oriented frameworks, GreyCat adds a timeout to remote function calls (default of 20s, configurable with --request_ttl)

@expose
fn heavy_computation() {
    while (true) {
        // noop
    }
}

any call through the HTTP API will then result in a timeout exception (and therefore rollback)

greycat serve --user=root
curl -X POST http://localhost:8080/project::heavy_computation
# {"message":"timeout","stack":[{"module":"project","function":"heavy_computation","line":3,"column":16}]}

timeout is similar for write and read function entrypoints; this enhances the server for multi-user experience

Long running functions? Task is the answer!

An in depth explanation can be found here

Env variables

GreyCat supports a wide range of environment variables that can either be defined on the cli or in an .env file

Command-Line Option Environment Variable Description
–log= GREYCAT_LOG=info Level of log (none,error,warn,info,trace)
–cache= GREYCAT_CACHE=(75% of memory) Size of cache in bytes (if no suffix, default is MiB)
–store= GREYCAT_STORE=1.0GB Size of store in bytes (if no suffix, default is MiB)
–store_paths= GREYCAT_STORE_PATHS=(null) Path leveraged to store zone files
–http_threads= GREYCAT_HTTP_THREADS=3 Number of workers dedicated to handle http connections
–workers= GREYCAT_WORKERS=(CPU count) Task workers, split into the small, medium and large classes
–workers_small= GREYCAT_WORKERS_SMALL=auto How many of --workers run small tasks, which RPC calls are (auto: 20%, at least 1 from 2 workers)
–workers_large= GREYCAT_WORKERS_LARGE=auto How many of --workers run large tasks. They also run medium and small ones when idle (auto: 10%)
–cache_ratio_small= GREYCAT_CACHE_RATIO_SMALL=0.40 Object cache of a small worker, relative to a medium one (must be > 0)
–cache_ratio_large= GREYCAT_CACHE_RATIO_LARGE=3.00 Object cache of a large worker, relative to a medium one (must be > 0)
–metrics_host_step= GREYCAT_METRICS_HOST_STEP=1min Period of the HostMetric record, when the host metric is on (GREYCAT_METRICS): host CPU, memory, disk, zones and worker classes
–port= GREYCAT_PORT=8080 Port used by serve command
–webroot= GREYCAT_WEBROOT=webroot Webroot to serve
–key= GREYCAT_KEY=(null) Path to private key
–keysafe= GREYCAT_KEYSAFE=(null) Pass for user pass
–user= GREYCAT_USER=(null) Impersonate mode to serve or run using a user name
–tz= GREYCAT_TZ=(null) Configure local timezone (see https://www.iana.org/time-zones, ex: Europe/Luxembourg)
–validity= GREYCAT_VALIDITY=1day Validity of connection cookie
–unsecure GREYCAT_UNSECURE=false Allows GreyCat tokens to be used behind an unsecure reverse-proxy (aka http)
–task_pool_capacity= GREYCAT_TASK_POOL_CAPACITY=10000 Maximum number of tasks in the queue
–request_pool_capacity= GREYCAT_REQUEST_POOL_CAPACITY=8192 Maximum number of connections served at once (a kept-alive connection holds a slot while idle)
–mode= GREYCAT_MODE=(null) Default mode (serve,run)
–keep_alive GREYCAT_KEEP_ALIVE=true Let clients reuse a connection across requests (Connection: keep-alive)
–backup_path= GREYCAT_BACKUP_PATH=backup Path to store and retrieve backups
–max_backup_files= GREYCAT_MAX_BACKUP_FILES=3 Maximum number of backup files to keep in backup_path
–defrag_ratio= GREYCAT_DEFRAG_RATIO=2.00 Blocks held per live block tolerated across data files before defrag (2.0 = half garbage) (must be > 1.0, a value <= 0 deactivates defrag)
–request_ttl= GREYCAT_REQUEST_TTL=20s Max HTTP request lifetime. An RPC call still queued or running is cancelled, an idle connection is closed
–ca_path= GREYCAT_CA_PATH=(null) Directory containing custom CA certificates to trust for SSL/TLS connections
–mcp_content= GREYCAT_MCP_CONTENT=both How MCP tools/call ships its payload: duplicated, structuredContent only, or content text only (both,structured,text)
–mcp_instructions= GREYCAT_MCP_INSTRUCTIONS=(null) Usage guidance returned to MCP clients in the initialize result
–openapi GREYCAT_OPENAPI=true Expose every @expose’d function in the OpenAPI v3 spec. When false, only @tag(“openapi”) ones are
–max_sse= GREYCAT_MAX_SSE=2048 Maximum number of open task event streams (GET /runtime::Task::events). Each holds a request pool slot
–max_sse_per_user= GREYCAT_MAX_SSE_PER_USER=16 Maximum number of open task event streams per user
–max_args_memory= GREYCAT_MAX_ARGS_MEMORY=1.05MB RPC arguments larger than this are streamed to the task’s arguments file instead of held in memory (if no suffix, default is MiB)
–metrics= GREYCAT_METRICS=(null) Metrics recorded, each to its own stream: host (host_metrics, HostMetric every --metrics_host_step), task (task_metrics, TaskMetric per task), http (http_metrics, HttpMetric per HTTP request). None when unset (comma-separated: host,task,http)
–metrics_http_fields= GREYCAT_METRICS_HTTP_FIELDS=(null) Personal data the HttpMetric records, when the http metric is on (GREYCAT_METRICS): ip (client address), forwarded_for (X-Forwarded-For header), user_agent (User-Agent header). None when unset (comma-separated: ip,forwarded_for,user_agent)
–metrics_http_flush_size= GREYCAT_METRICS_HTTP_FLUSH_SIZE=64.0KB HttpMetric records are buffered and written to http_metrics once they reach this size, 0 writes each one at once (if no suffix, default is MiB)
–metrics_http_flush_step= GREYCAT_METRICS_HTTP_FLUSH_STEP=1s Longest time an HttpMetric record stays buffered before it is written to http_metrics
–log_flush_size= GREYCAT_LOG_FLUSH_SIZE=64.0KB Log records are buffered and written to the log stream once they reach this size, 0 writes each one at once (if no suffix, default is MiB)
–log_flush_step= GREYCAT_LOG_FLUSH_STEP=1s Longest time a log record stays buffered before it is written to the log stream
–metrics_host_flush_size= GREYCAT_METRICS_HOST_FLUSH_SIZE=64.0KB HostMetric records are buffered and written to host_metrics once they reach this size, 0 writes each one at once (if no suffix, default is MiB)
–metrics_host_flush_step= GREYCAT_METRICS_HOST_FLUSH_STEP=1s Longest time a HostMetric record stays buffered before it is written to host_metrics
–metrics_task_flush_size= GREYCAT_METRICS_TASK_FLUSH_SIZE=64.0KB TaskMetric records are buffered and written to task_metrics once they reach this size, 0 writes each one at once (if no suffix, default is MiB)
–metrics_task_flush_step= GREYCAT_METRICS_TASK_FLUSH_STEP=1s Longest time a TaskMetric record stays buffered before it is written to task_metrics
–log_flush_on_error GREYCAT_LOG_FLUSH_ON_ERROR=false An error record writes the buffered log records to the log stream at once

Runtime info

The runtime module provides a RuntimeInfo object, returned by Runtime::info(), which contains all important information about the currently running GreyCat server

Name Type Description
version String GreyCat version
program_version String?
arch String
timezone TimeZone env specified tz else machine tz
license License
io_threads int
mem_total int Cache size (--cache)
mem_workers int Part of the cache shared by the task workers
small RuntimeInfoClass Task workers of the small class, which run RPC calls
medium RuntimeInfoClass Task workers of the medium class
large RuntimeInfoClass Task workers of the large class
disk_data_bytes int

Runtime Info Class

Name Type Description
workers int Workers of the class, 0 when its tasks run in the nearest class that has workers
mem_worker int Object cache budget of one worker of the class, in bytes
cache_ratio float Cache of one worker relative to a medium one (--cache_ratio_small, 1, --cache_ratio_large)

Store Stats

Runtime::store_stats() returns a StoreStats object describing the store. It reads every live block, so it lasts as long as a full backup.

Name Type Description
live_blocks int Blocks reachable from the store index and readable
live_bytes int Payload bytes those blocks hold
file_bytes int Bytes the zone files occupy on disk, fragmentation included
abi_bytes int Bytes of the ABI file
meta_bytes int Bytes the block index occupies
types Array Live blocks by type, the largest share of live_bytes first
damaged Array Blocks the index lists but that would not read
unreadable_zones Array Zones that could not be opened
worlds Array? The worlds, in the order World::list gives them