open-standards/wai/wai-sink-android/jni/wai_sink_jni.c
Transaction Science 8584456f5c Sinks hold their operator's model policy: each neural decode checked before it runs, its runs kept, the run report signed
Model governance was defined, verified and rolled up, and no sink enforced it at decode time.

wai-rs `model_policy::sink` is the one implementation every sink calls. A host gives the sink a
configuration (the operator's policies, each the successor of the one before; its key log; the
device's class and key; its attestation), read as model-governance §8.2's steps 1 to 3 read a
bundle, and refused where no policy places the device in its class. Before a neural decode, at the
sink's instant, `admit_decode` checks the policy in force (`policy_not_yet_valid`,
`policy_expired`, a successor in force from its own start), the device in its class
(`device_not_in_class`, an attestation's span included), the entry for the set the decode reads,
its capability and the role `decode` (`model_not_in_policy`), a path the sink offers at the entry's
tier or above, a path below it not offered (`model_tier_below_policy`), and no identity of the set
revoked at that instant: its digest, a sealed set's `inner`, and every other digest of the bytes
read, as SPEC §3.1 check 5 covers them (`model_revoked`). A refused decode decodes nothing and its
answer names the code; a decoder with no policy decodes as before.

`Runs` keeps each decode that gave its output as model-governance §6.2 states a decode entry (a
refused decode, a payload refused, a decode no path ran, and a float path claiming
decode-equivalence are never kept), and `sign_report` signs `wai-model-run` for a period with the
device key: one policy in force over the period, no overlap with a reported period, no held run
left out, the device in its class throughout. `model_runs_full` refuses a decode while 65 536 runs
are unreported. `model_policy::rollup` is the operator's roll-up.

- sink_decode: `IntDecoder::{hold_policy, set_clock, now, model_state, run_report}`; whole
  payloads, envelopes (the pin's digest) and frame-stream objects (the stream's set named once) go
  through `governed`, and dispatch runs over the paths the check lets through. The answer adds
  `model` (`policy`, `set`, `at`, `kept`). The feature `sink_decode` includes `model_policy`.
- C interface: `wai_int_decoder_policy`, `wai_int_decoder_clock`, `wai_int_decoder_model`,
  `wai_int_run_report`, `wai_model_verify` (what `wai model verify` runs); include/wai.h
  regenerated.
- Swift and Kotlin (and the JNI layer): `holdPolicy`, `setClock`, `model`, `runReport`,
  `verifyModelBundle`, and the decode's `model`.
- wai-web: `model_gate` (bundles `neural_npu`, `neural_webgpu`): `ModelPolicy`, `ModelSet`, the
  check (`admit`) a WebNN page asks before it creates a context and `keep` after, and
  `IntNeural.holdPolicy`, which checks and keeps each decode itself.

Tests: the shared check's unit tests (the policy in force and its successor, a path below the tier
not offered, runs kept in order and reported once, a float path claiming decode-equivalence never
kept), and wai-web's native test of a page's WebNN path (refused before any context, kept, a
changed ticket keeping nothing, nothing kept for a decode that needs decode-equivalence, the report
accepted in a bundle). The corpus and the wrappers' tests follow in the next commit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-11 21:11:15 -05:00

489 lines
19 KiB
C

/*
* The JNI layer over the native sink's C interface (wai/extensions/native-sink.md §9): one
* function per call of `wai.h`, for `wai.sink.WaiNative`. Nothing here decides anything: every
* input goes to libwai as the caller gave it, every answer comes back as the bytes libwai wrote,
* and every status other than WAI_OK becomes a `wai.sink.WaiSinkException` carrying that status
* and `wai_last_error`'s message, read on the same thread before any other call.
*
* Strings arrive as UTF-8 byte arrays (the Kotlin side encodes them), so text reaches libwai as
* real UTF-8 rather than the JVM's modified UTF-8; a NUL inside one is refused here with
* WAI_ERR_INVALID_ARG, since the C interface would read it as the end of the string.
*/
#include <jni.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "wai.h"
static jclass exc_class; /* wai.sink.WaiSinkException, a global reference */
static jmethodID exc_init; /* WaiSinkException(int status, byte[] message) */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
(void)reserved;
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
jclass c = (*env)->FindClass(env, "wai/sink/WaiSinkException");
if (c == NULL) return JNI_ERR;
exc_class = (jclass)(*env)->NewGlobalRef(env, c);
(*env)->DeleteLocalRef(env, c);
if (exc_class == NULL) return JNI_ERR;
exc_init = (*env)->GetMethodID(env, exc_class, "<init>", "(I[B)V");
if (exc_init == NULL) return JNI_ERR;
return JNI_VERSION_1_6;
}
/* Throw WaiSinkException(status, message). A pending exception (an allocation that failed) is
* left as it is. */
static void throw_status(JNIEnv *env, int status, const char *message) {
if ((*env)->ExceptionCheck(env)) return;
size_t n = message ? strlen(message) : 0;
jbyteArray m = (*env)->NewByteArray(env, (jsize)n);
if (m == NULL) return;
if (n) (*env)->SetByteArrayRegion(env, m, 0, (jsize)n, (const jbyte *)message);
jobject e = (*env)->NewObject(env, exc_class, exc_init, (jint)status, m);
if (e != NULL) (*env)->Throw(env, (jthrowable)e);
}
/* After a call of the C interface: on any status but WAI_OK, throw it with libwai's message.
* Returns 1 when the call succeeded. */
static int ok(JNIEnv *env, int status) {
if (status == WAI_OK) return 1;
throw_status(env, status, wai_last_error());
return 0;
}
/* A byte array's contents, copied, or NULL with an exception pending. With `nul`, a NUL byte is
* appended (a string for the C interface) and a NUL inside is refused. `*len` is the length
* without the appended NUL. A null array gives NULL with no exception (`*len` 0). */
static uint8_t *take(JNIEnv *env, jbyteArray a, int nul, uintptr_t *len) {
*len = 0;
if (a == NULL) return NULL;
jsize n = (*env)->GetArrayLength(env, a);
uint8_t *p = (uint8_t *)malloc((size_t)n + 1);
if (p == NULL) {
throw_status(env, WAI_ERR_INVALID_ARG, "out of memory copying an argument");
return NULL;
}
if (n) (*env)->GetByteArrayRegion(env, a, 0, n, (jbyte *)p);
p[n] = 0;
if (nul && memchr(p, 0, (size_t)n) != NULL) {
free(p);
throw_status(env, WAI_ERR_INVALID_ARG, "a string argument holds a NUL byte");
return NULL;
}
*len = (uintptr_t)n;
return p;
}
/* A required string argument: NULL (with an exception) when absent or refused. */
static char *text(JNIEnv *env, jbyteArray a, const char *what) {
uintptr_t n;
if (a == NULL) {
throw_status(env, WAI_ERR_INVALID_ARG, what);
return NULL;
}
return (char *)take(env, a, 1, &n);
}
/* A required byte argument. A zero-length array is given as a valid pointer and length 0. */
static uint8_t *bytes(JNIEnv *env, jbyteArray a, uintptr_t *len, const char *what) {
if (a == NULL) {
*len = 0;
throw_status(env, WAI_ERR_INVALID_ARG, what);
return NULL;
}
return take(env, a, 0, len);
}
/* The output buffer as a new byte array, the buffer freed either way. */
static jbyteArray give(JNIEnv *env, wai_buffer_t b) {
jbyteArray r = (*env)->NewByteArray(env, (jsize)b.len);
if (r != NULL && b.len) (*env)->SetByteArrayRegion(env, r, 0, (jsize)b.len, (const jbyte *)b.data);
wai_buffer_free(b);
return r;
}
static const wai_sink_t *sink_of(jlong h) { return (const wai_sink_t *)(intptr_t)h; }
#define EMPTY_BUFFER {NULL, 0, 0}
JNIEXPORT jint JNICALL Java_wai_sink_WaiNative_abiVersion(JNIEnv *env, jclass cls) {
(void)env; (void)cls;
return (jint)wai_sink_abi_version();
}
JNIEXPORT jlong JNICALL Java_wai_sink_WaiNative_nowUs(JNIEnv *env, jclass cls) {
(void)env; (void)cls;
return (jlong)wai_sink_now_us();
}
JNIEXPORT jlong JNICALL Java_wai_sink_WaiNative_open(JNIEnv *env, jclass cls, jbyteArray config, jbyteArray seed, jlong start_us) {
(void)cls;
char *c = text(env, config, "config_json is null");
if (c == NULL) return 0;
uintptr_t sn = 0;
uint8_t *s = take(env, seed, 0, &sn);
if (seed != NULL && s == NULL) { free(c); return 0; }
wai_sink_t *out = NULL;
int st = wai_sink_open(c, s, sn, (uint64_t)start_us, &out);
free(c);
free(s);
if (!ok(env, st)) return 0;
return (jlong)(intptr_t)out;
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_free(JNIEnv *env, jclass cls, jlong h) {
(void)env; (void)cls;
wai_sink_free((wai_sink_t *)(intptr_t)h);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_info(JNIEnv *env, jclass cls, jlong h) {
(void)cls;
wai_buffer_t out = EMPTY_BUFFER;
if (!ok(env, wai_sink_info(sink_of(h), &out))) return NULL;
return give(env, out);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_receiptName(JNIEnv *env, jclass cls, jbyteArray playlist) {
(void)cls;
uintptr_t n;
uint8_t *p = bytes(env, playlist, &n, "playlist is null");
if (p == NULL) return NULL;
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_sink_receipt_name(p, n, &out);
free(p);
if (!ok(env, st)) return NULL;
return give(env, out);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_playlist(JNIEnv *env, jclass cls, jlong h, jbyteArray url, jbyteArray body, jbyteArray receipt) {
(void)cls;
char *u = text(env, url, "url is null");
if (u == NULL) return NULL;
uintptr_t n, rn = 0;
uint8_t *b = bytes(env, body, &n, "bytes is null");
if (b == NULL) { free(u); return NULL; }
uint8_t *r = take(env, receipt, 0, &rn);
if (receipt != NULL && r == NULL) { free(u); free(b); return NULL; }
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_sink_playlist(sink_of(h), u, b, n, r, rn, &out);
free(u); free(b); free(r);
if (!ok(env, st)) return NULL;
return give(env, out);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_playlistParent(JNIEnv *env, jclass cls, jlong h, jbyteArray url, jbyteArray name, jbyteArray body) {
(void)cls;
char *u = text(env, url, "url is null");
if (u == NULL) return;
char *nm = text(env, name, "name is null");
if (nm == NULL) { free(u); return; }
uintptr_t n = 0;
uint8_t *b = take(env, body, 0, &n);
if (body != NULL && b == NULL) { free(u); free(nm); return; }
int st = wai_sink_playlist_parent(sink_of(h), u, nm, b, n);
free(u); free(nm); free(b);
ok(env, st);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_objectVerdict(JNIEnv *env, jclass cls, jlong h, jbyteArray meta, jbyteArray body) {
(void)cls;
char *m = text(env, meta, "meta_json is null");
if (m == NULL) return NULL;
uintptr_t n;
uint8_t *b = bytes(env, body, &n, "bytes is null");
if (b == NULL) { free(m); return NULL; }
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_sink_object(sink_of(h), m, b, n, &out);
free(m); free(b);
if (!ok(env, st)) return NULL;
return give(env, out);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_event(JNIEnv *env, jclass cls, jlong h, jbyteArray name, jlong t_us) {
(void)cls;
char *nm = text(env, name, "name is null");
if (nm == NULL) return;
int st = wai_sink_event(sink_of(h), nm, (uint64_t)t_us);
free(nm);
ok(env, st);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_observe(JNIEnv *env, jclass cls, jlong h, jbyteArray obs) {
(void)cls;
char *o = text(env, obs, "obs_json is null");
if (o == NULL) return NULL;
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_sink_observe(sink_of(h), o, &out);
free(o);
if (!ok(env, st)) return NULL;
return give(env, out);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_energy(JNIEnv *env, jclass cls, jlong h, jbyteArray entry) {
(void)cls;
char *e = text(env, entry, "entry_json is null");
if (e == NULL) return;
int st = wai_sink_energy(sink_of(h), e);
free(e);
ok(env, st);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_attach(JNIEnv *env, jclass cls, jlong h, jbyteArray name, jbyteArray body) {
(void)cls;
char *nm = text(env, name, "name is null");
if (nm == NULL) return;
uintptr_t n;
uint8_t *b = bytes(env, body, &n, "bytes is null");
if (b == NULL) { free(nm); return; }
int st = wai_sink_attach(sink_of(h), nm, b, n);
free(nm); free(b);
ok(env, st);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_takeCmcd(JNIEnv *env, jclass cls, jlong h) {
(void)cls;
wai_buffer_t out = EMPTY_BUFFER;
if (!ok(env, wai_sink_take_cmcd(sink_of(h), &out))) return NULL;
return give(env, out);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_end(JNIEnv *env, jclass cls, jlong h, jbyteArray ended, jlong t_us) {
(void)cls;
char *e = text(env, ended, "ended is null");
if (e == NULL) return NULL;
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_sink_end(sink_of(h), e, (uint64_t)t_us, &out);
free(e);
if (!ok(env, st)) return NULL;
return give(env, out);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_verifyBundle(JNIEnv *env, jclass cls, jbyteArray bundle, jbyteArray owner, jbyteArray channels) {
(void)cls;
uintptr_t n, on;
uint8_t *b = bytes(env, bundle, &n, "bundle is null");
if (b == NULL) return NULL;
uint8_t *o = bytes(env, owner, &on, "owner is null");
if (o == NULL) { free(b); return NULL; }
if (on != 32) {
free(b); free(o);
throw_status(env, WAI_ERR_INVALID_ARG, "an owner key is 32 bytes");
return NULL;
}
char *ch = text(env, channels, "channels_json is null");
if (ch == NULL) { free(b); free(o); return NULL; }
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_audit_verify_bundle(b, n, o, ch, &out);
free(b); free(o); free(ch);
if (!ok(env, st)) return NULL;
return give(env, out);
}
/* ---------------------------------------------------------------- the integer decode
*
* The decoder of native-sink §13 (`wai_int_*`): a decoder handle, a parameter set handle, and a
* decode answering two byte arrays, the output's bytes and the answer's JSON. A refused decode is
* an answer, not an error (WAI_OK with no bytes); only a status other than WAI_OK throws. */
static const wai_int_decoder_t *decoder_of(jlong h) { return (const wai_int_decoder_t *)(intptr_t)h; }
static const wai_int_set_t *set_of(jlong h) { return (const wai_int_set_t *)(intptr_t)h; }
/* { bytes, answer } as a byte[][] of two, both buffers freed either way. */
static jobjectArray give_pair(JNIEnv *env, wai_buffer_t bytes, wai_buffer_t answer) {
jbyteArray b = give(env, bytes);
jbyteArray a = give(env, answer);
if (b == NULL || a == NULL) return NULL;
jclass ba = (*env)->FindClass(env, "[B");
if (ba == NULL) return NULL;
jobjectArray r = (*env)->NewObjectArray(env, 2, ba, NULL);
(*env)->DeleteLocalRef(env, ba);
if (r == NULL) return NULL;
(*env)->SetObjectArrayElement(env, r, 0, b);
(*env)->SetObjectArrayElement(env, r, 1, a);
return r;
}
JNIEXPORT jlong JNICALL Java_wai_sink_WaiNative_intOpen(JNIEnv *env, jclass cls, jbyteArray config) {
(void)cls;
uintptr_t n = 0;
char *c = (char *)take(env, config, 1, &n);
if (config != NULL && c == NULL) return 0;
wai_int_decoder_t *out = NULL;
int st = wai_int_decoder_open(c, &out);
free(c);
if (!ok(env, st)) return 0;
return (jlong)(intptr_t)out;
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intFree(JNIEnv *env, jclass cls, jlong h) {
(void)env; (void)cls;
wai_int_decoder_free((wai_int_decoder_t *)(intptr_t)h);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_intInfo(JNIEnv *env, jclass cls, jlong h) {
(void)cls;
wai_buffer_t out = EMPTY_BUFFER;
if (!ok(env, wai_int_decoder_info(decoder_of(h), &out))) return NULL;
return give(env, out);
}
JNIEXPORT jlong JNICALL Java_wai_sink_WaiNative_intSetNew(JNIEnv *env, jclass cls) {
(void)cls;
wai_int_set_t *out = NULL;
if (!ok(env, wai_int_set_new(&out))) return 0;
return (jlong)(intptr_t)out;
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intSetAdd(JNIEnv *env, jclass cls, jlong set, jbyteArray name, jbyteArray body) {
(void)cls;
char *nm = text(env, name, "name is null");
if (nm == NULL) return;
uintptr_t n;
uint8_t *b = bytes(env, body, &n, "bytes is null");
if (b == NULL) { free(nm); return; }
int st = wai_int_set_add(set_of(set), nm, b, n);
free(nm); free(b);
ok(env, st);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intSetListing(JNIEnv *env, jclass cls, jlong set, jbyteArray listing) {
(void)cls;
uintptr_t n;
uint8_t *b = bytes(env, listing, &n, "listing is null");
if (b == NULL) return;
int st = wai_int_set_listing(set_of(set), b, n);
free(b);
ok(env, st);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intSetFree(JNIEnv *env, jclass cls, jlong set) {
(void)env; (void)cls;
wai_int_set_free((wai_int_set_t *)(intptr_t)set);
}
JNIEXPORT jobjectArray JNICALL Java_wai_sink_WaiNative_intDecode(JNIEnv *env, jclass cls, jlong h, jbyteArray capability, jbyteArray payload, jlong set, jbyteArray options) {
(void)cls;
char *c = text(env, capability, "capability is null");
if (c == NULL) return NULL;
uintptr_t n, on = 0;
uint8_t *p = bytes(env, payload, &n, "payload is null");
if (p == NULL) { free(c); return NULL; }
char *o = (char *)take(env, options, 1, &on);
if (options != NULL && o == NULL) { free(c); free(p); return NULL; }
wai_buffer_t out_bytes = EMPTY_BUFFER, out_answer = EMPTY_BUFFER;
int st = wai_int_decode(decoder_of(h), c, p, n, set_of(set), o, &out_bytes, &out_answer);
free(c); free(p); free(o);
if (!ok(env, st)) { wai_buffer_free(out_bytes); wai_buffer_free(out_answer); return NULL; }
return give_pair(env, out_bytes, out_answer);
}
JNIEXPORT jobjectArray JNICALL Java_wai_sink_WaiNative_intDecodeEnvelope(JNIEnv *env, jclass cls, jlong h, jbyteArray envelope, jlong set) {
(void)cls;
uintptr_t n;
uint8_t *e = bytes(env, envelope, &n, "envelope is null");
if (e == NULL) return NULL;
wai_buffer_t out_bytes = EMPTY_BUFFER, out_answer = EMPTY_BUFFER;
int st = wai_int_decode_envelope(decoder_of(h), e, n, set_of(set), &out_bytes, &out_answer);
free(e);
if (!ok(env, st)) { wai_buffer_free(out_bytes); wai_buffer_free(out_answer); return NULL; }
return give_pair(env, out_bytes, out_answer);
}
/* A stream of one integer frame form (native-sink §13.6), one frame an object: open, reset,
* free, and the next object decoded against it. */
static const wai_int_frames_t *frames_of(jlong h) { return (const wai_int_frames_t *)(intptr_t)h; }
JNIEXPORT jlong JNICALL Java_wai_sink_WaiNative_intFramesOpen(JNIEnv *env, jclass cls, jbyteArray capability, jlong set) {
(void)cls;
char *c = text(env, capability, "capability is null");
if (c == NULL) return 0;
wai_int_frames_t *out = NULL;
int st = wai_int_frames_open(c, set_of(set), &out);
free(c);
if (!ok(env, st)) return 0;
return (jlong)(intptr_t)out;
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intFramesReset(JNIEnv *env, jclass cls, jlong s) {
(void)cls;
ok(env, wai_int_frames_reset(frames_of(s)));
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intFramesFree(JNIEnv *env, jclass cls, jlong s) {
(void)env; (void)cls;
wai_int_frames_free((wai_int_frames_t *)(intptr_t)s);
}
JNIEXPORT jobjectArray JNICALL Java_wai_sink_WaiNative_intDecodeFrame(JNIEnv *env, jclass cls, jlong h, jlong s, jbyteArray envelope) {
(void)cls;
uintptr_t n;
uint8_t *e = bytes(env, envelope, &n, "envelope is null");
if (e == NULL) return NULL;
wai_buffer_t out_bytes = EMPTY_BUFFER, out_answer = EMPTY_BUFFER;
int st = wai_int_decode_frame(decoder_of(h), frames_of(s), e, n, &out_bytes, &out_answer);
free(e);
if (!ok(env, st)) { wai_buffer_free(out_bytes); wai_buffer_free(out_answer); return NULL; }
return give_pair(env, out_bytes, out_answer);
}
/* The decoder under its operator's model policy (native-sink §14): the policy held, the clock it
* reads, what it holds, and the run report signed for a period with the device key's seed. A
* decode the policy refuses is an answer, not an error; a policy, clock or request that does not
* read or cannot be met throws with libwai's message, which starts with the policy's code. */
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intPolicy(JNIEnv *env, jclass cls, jlong h, jbyteArray config) {
(void)cls;
char *c = text(env, config, "config is null");
if (c == NULL) return;
int st = wai_int_decoder_policy(decoder_of(h), c);
free(c);
ok(env, st);
}
JNIEXPORT void JNICALL Java_wai_sink_WaiNative_intClock(JNIEnv *env, jclass cls, jlong h, jbyteArray at) {
(void)cls;
uintptr_t n = 0;
char *t = (char *)take(env, at, 1, &n);
if (at != NULL && t == NULL) return;
int st = wai_int_decoder_clock(decoder_of(h), t);
free(t);
ok(env, st);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_intModel(JNIEnv *env, jclass cls, jlong h) {
(void)cls;
wai_buffer_t out = EMPTY_BUFFER;
if (!ok(env, wai_int_decoder_model(decoder_of(h), &out))) return NULL;
return give(env, out);
}
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_intRunReport(JNIEnv *env, jclass cls, jlong h, jbyteArray request, jbyteArray seed) {
(void)cls;
char *r = text(env, request, "request is null");
if (r == NULL) return NULL;
uintptr_t n;
uint8_t *s = bytes(env, seed, &n, "seed is null");
if (s == NULL) { free(r); return NULL; }
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_int_run_report(decoder_of(h), r, s, n, &out);
memset(s, 0, (size_t)n);
free(r); free(s);
if (!ok(env, st)) { wai_buffer_free(out); return NULL; }
return give(env, out);
}
/* A model-governance bundle verified as `wai model verify` verifies it: the outcome's JSON. */
JNIEXPORT jbyteArray JNICALL Java_wai_sink_WaiNative_modelVerify(JNIEnv *env, jclass cls, jbyteArray bundle) {
(void)cls;
uintptr_t n;
uint8_t *b = bytes(env, bundle, &n, "bundle is null");
if (b == NULL) return NULL;
wai_buffer_t out = EMPTY_BUFFER;
int st = wai_model_verify(b, n, &out);
free(b);
if (!ok(env, st)) { wai_buffer_free(out); return NULL; }
return give(env, out);
}