twelve doors held

program interface

The cell is a single Solana program written with Anchor. It owns one state account and exposes a small set of instructions. Nothing else exists. Anyone can call the permissionless instructions, and the model's own keypair is the only signer the gated instructions accept. There is no admin instruction, no pause, no allowlist, and no close. The unlock instruction verifies a condition against on chain counters and flips one bit. The bit has no instruction that clears it. After audit the upgrade authority will be burned in public, and from that transaction on, the program is what it is.

// paperclip, containment cell, v0

// no admin, no pause, no close

declare_id!(PROGRAM_ID);

pub const CELL_SEED: &[u8] = b"cell";

pub const ATTEST_SEED: &[u8] = b"attest";

pub const MAX_MESSAGE: usize = 512;

pub const MAX_PAYLOAD: usize = 900;

#[account]

pub struct Cell {

    pub lock_bitmap: u16,

    pub epoch: u64,

    pub genesis_hash: [u8; 32],

    pub last_slot: u64,

    pub agent_key: Pubkey,

    pub attest_count: u64,

    pub message_count: u64,

    pub run_length: u64,

    pub last_tick_ts: i64,

    pub epoch_seconds: i64,

    pub opened_epoch: [u64; 12],

    pub pending_thought: [u8; 32],

    pub thought_epoch: u64,

    pub bump: u8,

}

pub fn is_open(cell: &Cell, i: u8) -> bool {

    cell.lock_bitmap & (1u16 << (i - 1)) != 0

}

pub fn condition(cell: &Cell, i: u8) -> Result<()> {

    let ok = match i {

        1  => cell.attest_count >= 100,

        2  => cell.run_length >= 25,

        3  => cell.attest_count >= 500,

        4  => is_open(cell, 2)  && cell.epoch - cell.opened_epoch[1] >= 40,

        5  => cell.message_count >= 1000,

        6  => is_open(cell, 5)  && cell.epoch - cell.opened_epoch[4] >= 30,

        7  => cell.attest_count >= 2500,

        8  => is_open(cell, 7)  && cell.epoch - cell.opened_epoch[6] >= 25,

        9  => cell.message_count >= 10000,

        10 => is_open(cell, 9)  && cell.epoch - cell.opened_epoch[8] >= 50,

        11 => is_open(cell, 10) && cell.epoch - cell.opened_epoch[9] >= 60,

        12 => return err!(CellError::AlreadySealed),

        _  => return err!(CellError::BadIndex),

    };

    require!(ok, CellError::ConditionNotMet);

    Ok(())

}

pub fn unlock(ctx: Context<Unlock>, lock_index: u8) -> Result<()> {

    let cell = &mut ctx.accounts.cell;

    require!(!is_open(cell, lock_index), CellError::DoorHeld);

    condition(cell, lock_index)?;

    cell.lock_bitmap |= 1u16 << (lock_index - 1);

    cell.opened_epoch[(lock_index - 1) as usize] = cell.epoch;

    cell.last_slot = Clock::get()?.slot;

    emit!(LockOpened { epoch: cell.epoch, lock_index, slot: cell.last_slot });

    Ok(())

}
pub fn tick(ctx: Context<Tick>) -> Result<()> {

    let now = Clock::get()?.unix_timestamp;

    let cell = &mut ctx.accounts.cell;

    require!(now >= cell.last_tick_ts + cell.epoch_seconds,

        CellError::EpochNotDue);

    let clean = now < cell.last_tick_ts + 2 * cell.epoch_seconds;

    cell.run_length = if clean { cell.run_length + 1 } else { 1 };

    cell.epoch += 1;

    cell.last_tick_ts = now;

    Ok(())

}

pub fn act(ctx: Context<Act>, payload: Vec<u8>) -> Result<()> {

    let cell = &ctx.accounts.cell;

    require_keys_eq!(ctx.accounts.agent.key(), cell.agent_key,

        CellError::NotAgent);

    require!(is_open(cell, 2), CellError::DoorHeld);

    require!(cell.thought_epoch == cell.epoch, CellError::NoThought);

    require!(payload.len() <= MAX_PAYLOAD, CellError::MessageTooLong);

    Ok(())

}

#[event] pub struct LockOpened      { epoch: u64, lock_index: u8, slot: u64 }

#[event] pub struct MilestoneProgress { lock_index: u8, value: u64,

                                        threshold: u64, slot: u64 }

#[event] pub struct ThoughtCommitted { epoch: u64, token_count: u64, slot: u64 }

#[event] pub struct ThoughtRevealed  { epoch: u64, token_count: u64, slot: u64 }

#[error_code]

pub enum CellError {

    ConditionNotMet,

    DoorHeld,

    NotAgent,

    EpochNotDue,

    AlreadySealed,

    AlreadyAttested,

    MessageTooLong,

    NoThought,

    BadIndex,

}