sokr 0.1.1

SOKR core — immutable C ABI surface for substrate plugins
Documentation
# RFC 0001: Plugin Interface

**Status:** Open for comment (4 week period)
**Opened:** 2026-04-16
**Closes:** 2026-05-14 (4 weeks)
**Tracking:** https://github.com/sokr-rs/sokr/discussions/2

---

## Summary

This RFC defines the core C ABI plugin interface for SOKR — the
Capability, Dispatch, and Completion contract that all substrate
plugins must implement, plus the version handshake protocol.

---

## Motivation

SOKR's core philosophy is: immutable sovereign core, everything else
a plugin. The plugin interface is the contract that makes this possible.
Getting it right before any code is written is more important than any
other decision in Phase 0 — a wrong interface here costs every plugin
author a breaking change at v0.2.x.

---

## The Interface

### Three Functions

Every substrate plugin implements exactly three functions:

```c
// Can this substrate fulfill this computation?
SokrResult sokr_capability(
    const SokrCapabilityQuery* query,
    SokrCapabilityResponse*    response
);

// Fulfill it.
SokrResult sokr_dispatch(
    const SokrDispatchRequest* request,
    SokrDispatchResponse*      response
);

// Signal when fulfilled.
SokrResult sokr_completion(
    const SokrCompletionQuery*  query,
    SokrCompletionSignal*       signal
);
```

Plus one lifecycle function:

```c
// Called by core when plugin is deregistered.
void sokr_destroy(void);
```

### Type Definitions

```c
typedef struct {
    uint32_t major;
    uint32_t minor;
    uint32_t patch;
} SokrVersion;

typedef enum {
    SOKR_OK                    = 0,
    SOKR_CAPABILITY_DENIED     = 1,
    SOKR_DISPATCH_FAILED       = 2,
    SOKR_TIMEOUT               = 3,
    SOKR_VERSION_MISMATCH      = 4,
    SOKR_NO_CAPABLE_SUBSTRATE  = 5,
    SOKR_INVALID_INPUT         = 6,
    SOKR_INVALID_IR            = 7,
    SOKR_NOT_FOUND             = 8,
    SOKR_REGISTRY_FULL         = 9,
} SokrResult;

typedef struct {
    uint8_t  bytes[16];           // opaque 128-bit ID
} SokrComputationId;

typedef struct {
    SokrComputationId  computation_id;
    uint32_t           ir_format;     // IR format identifier
    const uint8_t*     ir_data;       // pointer to IR bytes
    size_t             ir_data_len;   // length of IR bytes
} SokrCapabilityQuery;

typedef struct {
    SokrResult  result;
    uint32_t    substrate_id;
    uint64_t    estimated_latency_ns;
} SokrCapabilityResponse;

typedef struct {
    SokrComputationId  computation_id;
    uint32_t           substrate_id;
    const uint8_t*     ir_data;
    size_t             ir_data_len;
    const uint8_t*     params;        // optional dispatch parameters
    size_t             params_len;
} SokrDispatchRequest;

typedef uint64_t SokrCompletionToken;  // opaque handle

typedef struct {
    SokrResult           result;
    SokrCompletionToken  completion_token;
} SokrDispatchResponse;

typedef struct {
    SokrCompletionToken  completion_token;
    uint64_t             timeout_ns;   // 0 = non-blocking poll
} SokrCompletionQuery;

typedef enum {
    SOKR_COMPLETION_PENDING  = 0,
    SOKR_COMPLETION_COMPLETE = 1,
    SOKR_COMPLETION_FAILED   = 2,
    SOKR_COMPLETION_TIMEDOUT = 3,
} SokrCompletionSignal;

typedef struct {
    SokrVersion   version;
    SokrResult  (*capability_fn)(const SokrCapabilityQuery*, SokrCapabilityResponse*);
    SokrResult  (*dispatch_fn)(const SokrDispatchRequest*, SokrDispatchResponse*);
    SokrResult  (*completion_fn)(const SokrCompletionQuery*, SokrCompletionSignal*);
    void        (*destroy_fn)(void);
} SokrSubstratePlugin;
```

### IR Format Identifiers

IR format is a `uint32_t` magic number declared by the IR plugin:

| IR | Identifier |
|---|---|
| SPIR-V | `0x53505256` |
| PTX (NVIDIA) | `0x50545800` |
| OpenQASM 3 | `0x4F51334D` |
| Spike graph | `0x53504B45` |
| Optical circuit | `0x4F50544C` |
| SOKR-native (future) | TBD |

---

## Version Handshake Protocol

### Compatibility Rules

- `major` must match exactly — different majors are incompatible
- Plugin `minor` must be ≤ core `minor` — older plugins run on newer core
- Plugin `minor` > core `minor` — newer plugin on older core is accepted
  (forward compatibility: core ignores fields it does not understand)
- `patch` is irrelevant to compatibility

### Negotiation Sequence

1. Core calls `plugin.version_fn()` during registration
2. Plugin returns its compiled-in `SokrVersion`
3. Core compares against `SokrVersion::current()`
4. Incompatible → `SOKR_VERSION_MISMATCH` returned, plugin not registered
5. Compatible → plugin assigned a `substrate_id`, registration succeeds

### Version Bump Triggers

| Change | Version bump |
|---|---|
| Add a field to any ABI struct | `minor` |
| Remove or reorder a field | `major` |
| Add a new `SokrResult` variant | `minor` |
| Change function signature | `major` |
| Bug fix with no ABI change | `patch` |

---

## Ownership Semantics

- All pointers in query/request structs are **borrowed** — the plugin
  must not free them and must not retain them after the function returns
- All pointers in response structs are **owned by the caller** — the
  caller allocates, the plugin writes into them
- `SokrCompletionToken` is owned by the caller — the plugin must not
  free it; the core manages token lifetime
- `destroy_fn` is called exactly once, by the core, on deregistration

## Thread Safety Contract

- `sokr_capability()` — safe to call concurrently from multiple threads
- `sokr_dispatch()` — safe to call concurrently from multiple threads
- `sokr_completion()` — safe to call concurrently from multiple threads
- `sokr_register_substrate()` — NOT thread-safe, call from one thread only
- `sokr_deregister_substrate()` — NOT thread-safe, call from one thread only

---

## Alternatives Considered

### Four functions instead of three
Adding a `sokr_cancel()` function was considered. Rejected: cancellation
semantics differ too much across substrates (QPU jobs cannot be cancelled
once submitted). Cancellation is a dispatch policy concern, not a core
concern. A plugin can expose cancellation through the `params` field of
`SokrDispatchRequest` if needed.

### Single `sokr_execute()` combining all three
Rejected: conflates synchronous and asynchronous substrates. A
photonic substrate may have seconds of latency. A CPU substrate is
immediate. Separating dispatch from completion allows the caller to
do other work between them.

### Rust trait instead of C ABI
Rejected: would limit plugins to Rust. The sovereignty principle
requires any language to implement a plugin without depending on the
Rust toolchain.

---

## Open Questions

These are the questions we are asking the community to address during
the comment period. Every question will be resolved with either a spec
change or a documented rationale for keeping the current design.

1. Is `uint64_t` sufficient for `SokrCompletionToken` or should it be
   128 bits to reduce collision probability in long-running systems?

2. Should `estimated_latency_ns` in `SokrCapabilityResponse` be
   mandatory or optional (zero = unknown)?

3. Should `params` in `SokrDispatchRequest` have a defined schema, or
   remain fully opaque bytes defined by the substrate plugin?

4. Is the thread safety contract sufficient, or do we need a reader-writer
   lock model for the plugin registry?

5. Should `sokr_list_substrates()` be part of the core ABI or a
   separate introspection plugin?

---

## Decision Log

*This section is populated as feedback is received and decisions are made.*

| Date | Question | Decision | Rationale |
|---|---|---|---|
|||||

---

*Copyright 2026 The SOKR Project — MIT OR Apache-2.0*