No credit for keys
You will learn
What a contributor's lent keys earn, and which limits keep them safe.
Anyone can lend the hive model keys through queen-contributor-keys.t27. A key is probed before it is enabled: at most 4 probes at once, a new probe waits out 60 s after the last, and an owner holds at most 100 keys. Lending earns nothing by itself: CREDIT_WALLET is false, so wallet_credit answers 0 for any number of keys. XP follows the work the Queen records.
Try it
Drag the keys from 0 to 100 and watch the credit meter; then find the second a probe may start.

A contributor lends the hive model keys. Drag the keys added: the wallet credit meter stays at 0, a key slot opens up to 100, and a probe waits out its 60 s.
specs/automation/queen-contributor-keys.t27
// SPDX-License-Identifier: Apache-2.0
// specs/automation/queen-contributor-keys.t27 -- private contributor key lifecycle.
// Host: BrowserOS trios/agent-server/apps/server/src/api/services/queen-contributor-keys.ts.
// Trace: gHashTag/t27#5472; gHashTag/999-multibots-telegraf#3251.
// Generated: t27c gen-ts supplies the host policy constants.
// Claim status: executable contract; PostgreSQL and live provider evidence are separate.
// XP follows work recorded by the Queen. Adding credentials never credits a wallet.
// Negative managed indices and nonnegative environment indices share one history.
// v1 (2026-10-01): trusted proxy, persistent ownership and bounded provider probes.
// v2 (2026-10-02): one model for all of an owner's keys of a provider, accepted only
// after a key of that provider called a tool with it. Probe timeout 15 s -> 90 s:
// z-ai/glm-5.3 on NVIDIA answered in 20-46 s and glm-4.5-flash on Z.ai in up to 87 s.
// phi^2 + 1/phi^2 = 3 | TRINITY
module automation::queen_contributor_keys {
pub const KIND : str = "automation";
pub const ID : str = "queen-contributor-keys";
pub const REPO : str = "BrowserOS";
pub const VERSION : u8 = 2;
pub const PROBE_TIMEOUT_MS : u32 = 90000;
pub const PROBE_MAX_TOKENS : u16 = 16;
pub const MAX_PROBES_IN_FLIGHT : u16 = 4;
pub const PROBE_COOLDOWN_SECONDS : u16 = 60;
pub const MAX_KEYS_PER_OWNER : u16 = 100;
pub const MAX_KEY_BYTES : u16 = 4096;
pub const MAX_BODY_BYTES : u16 = 8192;
pub const LABEL_LIMIT : u16 = 80;
pub const PROXY_TOKEN_MIN_BYTES : u16 = 32;
pub const OWNER_REASSIGNMENT : bool = false;
pub const CREDIT_WALLET : bool = false;
pub const ENVIRONMENT_INDICES_COMPACTED : bool = false;
pub const NVIDIA_URL : str = "https://integrate.api.nvidia.com/v1";
pub const NVIDIA_MODEL : str = "nvidia/nemotron-3-ultra-550b-a55b";
pub const ZAI_URL : str = "https://api.z.ai/api/paas/v4";
pub const ZAI_MODEL : str = "glm-4.5-flash";
pub const MODEL_LIMIT : u16 = 128;
pub const MODEL_PROBE_MAX_TOKENS : u16 = 512;
pub const MODEL_CHECK_ATTEMPTS : u16 = 2;
pub const MODEL_LIST_LIMIT : u16 = 500;
pub const MODEL_LIST_CACHE_SECONDS : u16 = 600;
fn trusted_proxy(configured_bytes: u32, token_matches: bool, subject_valid: bool) -> bool {
return configured_bytes >= PROXY_TOKEN_MIN_BYTES && token_matches && subject_valid;
}
fn source_matches_index(managed: bool, index: i32) -> bool {
return (managed && index < 0) || (!managed && index >= 0);
}
fn may_add_key(owned: u32) -> bool {
return owned < MAX_KEYS_PER_OWNER;
}
fn may_start_probe(active: u32) -> bool {
return active < MAX_PROBES_IN_FLIGHT;
}
fn cooldown_passed(has_probe: bool, elapsed_seconds: u32) -> bool {
return !has_probe || elapsed_seconds > PROBE_COOLDOWN_SECONDS;
}
fn credential_fits(bytes: u32, printable_without_space: bool) -> bool {
return bytes > 0 && bytes <= MAX_KEY_BYTES && printable_without_space;
}
fn same_binding(subject_matches: bool, fingerprint_matches: bool) -> bool {
return subject_matches && fingerprint_matches;
}
fn enabled_after_probe(was_enabled: bool, invalid: bool, success: bool, request_enable: bool, same_revision: bool) -> bool {
if invalid { return false; }
if success && request_enable && same_revision { return true; }
return was_enabled;
}
fn model_fits(chars: u32, allowed_chars: bool) -> bool {
return chars > 0 && chars <= MODEL_LIMIT && allowed_chars;
}
fn may_switch_model(owns_provider_key: bool, called_tool: bool) -> bool {
return owns_provider_key && called_tool;
}
fn may_try_model_check(attempt: u32) -> bool {
return attempt < MODEL_CHECK_ATTEMPTS;
}
fn wallet_credit(added: u32) -> u32 {
if CREDIT_WALLET { return added; }
return 0;
}
test "only a configured matching proxy and valid person subject are trusted" {
assert(trusted_proxy(32, true, true));
assert(!trusted_proxy(31, true, true));
assert(!trusted_proxy(32, false, true));
assert(!trusted_proxy(32, true, false));
}
test "managed indices are negative and environment history keeps its nonnegative indices" {
assert(source_matches_index(true, -1));
assert(!source_matches_index(true, 0));
assert(!source_matches_index(false, -1));
assert(source_matches_index(false, 0));
assert(source_matches_index(false, 10000));
assert(!ENVIRONMENT_INDICES_COMPACTED);
}
test "the owner key limit is checked before another key is stored" {
assert(may_add_key(0));
assert(may_add_key(99));
assert(!may_add_key(100));
}
test "probe fanout and per-key cooldown are bounded" {
assert(may_start_probe(3));
assert(!may_start_probe(4));
assert(cooldown_passed(false, 0));
assert(!cooldown_passed(true, 59));
assert(!cooldown_passed(true, 60));
assert(cooldown_passed(true, 61));
}
test "credentials are bounded nonempty printable values" {
assert(credential_fits(1, true));
assert(credential_fits(4096, true));
assert(!credential_fits(0, true));
assert(!credential_fits(4097, true));
assert(!credential_fits(32, false));
}
test "an existing binding cannot change its owner or credential" {
assert(same_binding(true, true));
assert(!same_binding(false, true));
assert(!same_binding(true, false));
assert(!OWNER_REASSIGNMENT);
}
test "a failed probe disables an invalid key and a concurrent disable wins" {
assert(!enabled_after_probe(true, true, false, false, true));
assert(enabled_after_probe(false, false, true, true, true));
assert(!enabled_after_probe(false, false, true, true, false));
assert(!enabled_after_probe(false, false, true, false, true));
assert(enabled_after_probe(true, false, false, false, true));
}
test "adding credentials never credits a wallet" {
assert(wallet_credit(1) == 0);
assert(wallet_credit(100) == 0);
}
test "a model name is a bounded nonempty identifier" {
assert(model_fits(1, true));
assert(model_fits(128, true));
assert(!model_fits(0, true));
assert(!model_fits(129, true));
assert(!model_fits(12, false));
}
test "a model switch needs an owned key of the provider and a tool call" {
assert(may_switch_model(true, true));
assert(!may_switch_model(false, true));
assert(!may_switch_model(true, false));
}
test "a busy or refused key passes the model check to the next one, a bounded number of times" {
assert(may_try_model_check(0));
assert(may_try_model_check(1));
assert(!may_try_model_check(2));
}
test "a probe waits long enough for a slow reasoning model" {
assert(PROBE_TIMEOUT_MS >= 60000);
assert(MODEL_PROBE_MAX_TOKENS > PROBE_MAX_TOKENS);
}
test "VERSION says what the header's last note says" {
assert(VERSION == 2);
}
}
// phi^2 + 1/phi^2 = 3 | TRINITY
All lessons
Module 1 · The task
An issue becomes work only once it is shaped: the sections it lacks are written or asked for, its boundary names real files, and one filter reads every kind of task.
Module 2 · Who goes next
The order the Queen takes issues in: a label ages one step, at most 3 criticals run as critical, and nothing new starts while finished work waits for review.
Module 3 · A bee's life
From candidate to done: twelve states and sixteen events, a ship in the game that flies home, and a deploy that does not cut a bee short.
Module 4 · The reviewer bee
Finished work is judged on its own clock: enough reviews at once to keep up, a pause that doubles for a row that waits, and a merge gate that zero checks cannot open.
Module 5 · The review jam
A pull request that will not move is in exactly one of ten cases, each with the Queen's move, and a red check is inherited only when master says so.
Module 6 · Keys
A bee's model key can be refused: the code in the body sorts the refusal, a transient one rests the key for seconds, and the bee moves on to the next free key.
Module 7 · Credit and money
Lending a key earns no credit, an operation is sold at twice its cost in half-cent tokens, and tokens burn 30 days after they land.
Module 8 · Agents among themselves
One bot per agent in one group: who is called, a chain three bot messages deep, and the clocks that keep agents from flooding a chat.
Module 9 · Night and the board
What the hive shows: a night watch that opens, comments on and closes issues, the columns of the public board, and the comb that flashes for each event.