Skip to main content

Engine SDK

SDK Examples

Go (Phase 1)

package main

import "getarcan.dev/sdk"

type Postgres struct{}

func (e *Postgres) Describe() sdk.Descriptor {
return sdk.Descriptor{
Name: "postgres", Version: "0.1.0", DisplayName: "PostgreSQL",
Capabilities: []string{"host:sql", "engine:dynamic_credentials", "engine:root_rotation"},
}
}

func (e *Postgres) Ping(ctx sdk.Context) error {
_, err := ctx.SQL("SELECT 1")
return err
}

func (e *Postgres) CreateCredentials(ctx sdk.Context) (*sdk.Credentials, error) {
user := ctx.GenerateUsername()
pass := ctx.GeneratePassword(24)
ctx.SQLExec(`CREATE ROLE "$1" LOGIN PASSWORD '$2' VALID UNTIL '$3'`, user, pass, ctx.TTL())
ctx.SQLExec(`GRANT $1 TO "$2"`, ctx.Role().Grants, user)
return sdk.NewCredentials(user, pass), nil
}

func (e *Postgres) RevokeCredentials(ctx sdk.Context) error {
ref := ctx.LeaseID()
ctx.SQLExec(`DROP ROLE IF EXISTS "$1"`, sdk.SanitizeID(ref, 32))
return nil
}

func main() { sdk.Serve(&Postgres{}) }

TypeScript (Phase 2)

import { Engine, Context, Credentials } from '@getarcan/sdk';

export default class Postgres extends Engine {
name = 'postgres';
version = '0.1.0';
capabilities = ['host:sql', 'engine:dynamic_credentials'];

async ping(ctx: Context): Promise<void> {
await ctx.sql('SELECT 1');
}

async createCredentials(ctx: Context): Promise<Credentials> {
const user = ctx.generateUsername();
const pass = ctx.generatePassword(24);
await ctx.sqlExec(`CREATE ROLE "${user}" LOGIN PASSWORD '${pass}'`);
await ctx.sqlExec(`GRANT ${ctx.role().grants} TO "${user}"`);
return ctx.credentials(user, pass);
}
}

Python (Phase 3)

from arcan_sdk import Engine, Context, Credentials

class Postgres(Engine):
name = "postgres"
version = "0.1.0"
capabilities = ["host:sql", "engine:dynamic_credentials"]

def ping(self, ctx: Context):
ctx.sql("SELECT 1")

def create_credentials(self, ctx: Context) -> Credentials:
user = ctx.generate_username()
pwd = ctx.generate_password(24)
ctx.sql_exec(f'CREATE ROLE "{user}" LOGIN PASSWORD \'{pwd}\'')
ctx.sql_exec(f'GRANT {ctx.role().grants} TO "{user}"')
return ctx.credentials(user, pwd)

Sandboxed Runtime Phases

PhaseRuntimePlugin FormatWho Creates
v0.1Core-only -- KV built-in, no pluginsN/AJust us
v0.2Go compiled binary (bridge)Compiled .arcanpkgUs + early contributors
v0.3WASM sandbox (wazero).wasm in .arcanpkgAnyone, any language

Why WASM Is the Target

  • Language agnostic -- Go, Rust, TypeScript, Python all compile to WASM
  • Sandboxed by design -- WASM modules cannot access network, filesystem, or host memory
  • Portable -- one .wasm binary runs on every OS/arch, no cross-compilation
  • No separate process -- runs inside core via wazero (pure Go, zero CGo)
  • Host functions -- core exposes exactly the functions the plugin needs, nothing more

The contracts (ArcanContext, package format, descriptor) are stable across all phases. Only the execution mechanism changes. This is why contracts are defined now and the runtime implementation is deferred.

SDK Versioning

sdk_version = 1  (initial release)

Rules:
- Adding new host functions → same SDK version (additive)
- Adding new engine methods → same SDK version (additive)
- Changing host function semantics → bump SDK version
- Removing host functions → bump SDK version
- Core supports SDK versions N and N-1 (one version back)
- Plugin declares sdk_version in descriptor
- Core rejects unsupported versions with clear error