Cosmic Bull

Realm on pearl-1

timelock_guardian

gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/timelock_guardian

realmpipeline-applicationsecurity-delayed-execution

RenderedSourceCall builderState

curated

Delayed-execution guardian: queue, delay window, execute or cancel. GitHub-pipeline application #2.

Identity

Import pathgno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/timelock_guardian
Kindrealm (/r/)
Chainpearl-1
Namespaceg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Realm addressg1w06mqv4h53vtfgfeyz2eglplpwfpytcqqqjwkh derived, never confirmed against the realm

Provenance

chain-attested
Deployed at height602,213
Deploy transaction6db3f74c764523f8ea1d82f7cd419639a8bb597494b170a8d2ea56bc8779efbb look it up on the RPC
Deployerg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Gas used38,920,796
Storage37,342 bytes, deposit 3734200ugnot
Files on chaingnomod.toml timelock_guardian.gno
Deployed bytestimelock_guardian.gno — 24,983 bytes
sha25672ca7049496257e6fb00216aa027736fa90d41c1dbc91cf8059fc8fd29080f07

Do not take the hash above on trust. $download returns the bytes pearl-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:

curl -sS 'https://pearl.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/timelock_guardian$download&file=timelock_guardian.gno' | shasum -a 256

Expected: 72ca7049496257e6fb00216aa027736fa90d41c1dbc91cf8059fc8fd29080f07 — 24,983 bytes. This was checked for all 21 packages while building this site's architecture record; every one matched. Use curl: pearl's edge answers Python's default user-agent with HTTP 403.

API

chain-derived 15 exported functions, 2 types.

Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:

AcceptTargetOwnershipCancelExecuteExpireGetActionGetPendingGetTargetIsExecutedIsReadyRegisterTargetRenderScheduleSetGuardianTransferTargetOwnershipVeto

Overview

This package carries no package doc comment on chain, so there is nothing for vm/qdoc to return and gnoweb's $help Overview is empty. Deployed bytes are immutable, so this cannot be repaired in place — see catalog/DISCOVERY_APIDOCS.md §2.4.

Imports

Constants and variables

const (
	// MinDelayFloor is the smallest minimum delay a target may register.
	// A timelock with a 1-second delay protects nothing.
	MinDelayFloor = int64(60)

	// MaxDelay bounds both registered minimum delays and per-action
	// delays. Also the overflow guard: MaxDelay seconds in nanoseconds
	// is far below int64 range, so ExecuteAfter arithmetic cannot wrap
	// into the past.
	MaxDelay = int64(10 * 365 * 24 * 3600) // 10 years

	// GracePeriod is how long an action stays executable once ready.
	// After it, the action expires: something scheduled and forgotten
	// cannot be sprung on a target years later.
	GracePeriod = int64(30 * 24 * 3600) // 30 days

	// Quotas are PER-OWNER only (re-audit round 3): any global cap is a
	// shared resource a few sybil accounts can exhaust forever (10y
	// delays defeat expiry sweeping), bricking every other tenant. With
	// per-owner quotas an attacker only ever consumes their own budget;
	// state growth is priced in gas and funded accounts.
	MaxTargetsPerOwner  = 10
	MaxPendingPerTarget = 20
	MaxTargetNameLen    = 64
	MaxDataLen          = 2000
	MaxRenderActions    = 100
	// RenderIndexCap bounds the render-ordering index (fix Y4): the
	// index self-trims to this size, so reaping an entry from it is a
	// bounded scan no matter how many actions the realm has ever seen.
	// Records older than the window stay in state (GetAction/IsExecuted
	// are map reads and permanent); they only leave the front page.
	RenderIndexCap = 2 * MaxRenderActions
)

Types

type Action

type Action struct {
	ID           string
	Target       string
	Creator      address
	Data         string // encoded call data or description of the action
	ScheduledAt  time.Time
	ExecuteAfter time.Time
	ExpiresAt    time.Time // ExecuteAfter + GracePeriod; not executable after
	Executed     bool
}

Action represents a scheduled operation that can only execute after its delay has elapsed, and only within its grace window.

State model (re-audit 2026-09-02): only PENDING and EXECUTED actions are stored. Executed records are permanent attestations consumers check via IsExecuted. Cancelled, vetoed, and expired actions are REAPED from state — their history lives in emitted events — so the live-action cap bounds live exposure and can never be consumed permanently by schedule/cancel cycling.

Exported fieldTypeDoc
IDstring
Targetstring
Creatoraddress
Datastringencoded call data or description of the action
ScheduledAttime.Time
ExecuteAftertime.Time
ExpiresAttime.TimeExecuteAfter + GracePeriod; not executable after
Executedbool

type TargetConfig

type TargetConfig struct {
	Name     string
	Owner    address
	Guardian address // empty = no guardian
	MinDelay int64   // seconds; every action for this target waits at least this
	// PendingOwner is the offered-but-not-accepted new owner (fix Y5:
	// ownership moves in two steps, so a stranger can never have a
	// target — and its quota slot and pending obligations — dumped on
	// them without consenting).
	PendingOwner address
}

TargetConfig binds a named target to the only address allowed to schedule actions against it, an enforced minimum delay, and an optional guardian who can veto pending actions. Without this binding a timelock attests nothing: anyone could schedule their own short-delay action against any name and "execute" it.

Exported fieldTypeDoc
Namestring
Owneraddress
Guardianaddressempty = no guardian
MinDelayint64seconds; every action for this target waits at least this
PendingOwneraddressPendingOwner is the offered-but-not-accepted new owner (fix Y5: ownership moves in two steps, so a stranger can never have a target — and its quota slot and pending obligations — dumped on them without consenting).

Functions

AcceptTargetOwnership

func AcceptTargetOwnership(cur realm, targetName string)

Crossing function. Callable from a transaction via MsgCall, and from another realm as AcceptTargetOwnership(cross(cur), ...).

AcceptTargetOwnership completes a pending ownership offer; only the nominee can accept. The nominee's quota is checked HERE — consent time — so an offer can never overfill an account that did not agree to carry it.

Cancel

func Cancel(cur realm, actionID string) string

Crossing function. Callable from a transaction via MsgCall, and from another realm as Cancel(cross(cur), ...).

Cancel removes a pending action. Only the target's CURRENT owner can cancel (re-audit: the scheduling creator's rights must not survive an ownership transfer). The record is reaped; history is the event.

Execute

func Execute(cur realm, actionID string) string

Crossing function. Callable from a transaction via MsgCall, and from another realm as Execute(cross(cur), ...).

Execute marks an action as executed. Anyone can call this — the timelock is the protection, not the executor's identity. The action must exist, be pending, its delay elapsed, and its grace window not yet expired.

Expire

func Expire(cur realm, actionID string) string

Crossing function. Callable from a transaction via MsgCall, and from another realm as Expire(cross(cur), ...).

Expire reaps a provably expired action. Permissionless (fix Y2): an expired action decides nothing — reaping it only writes down what the clock already decided — so anyone may free the quota slot it holds. This is the recovery valve that makes a wedged target impossible: before it, a phantom expired entry consumed quota and blocked SetGuardian until a global sweep happened to reach it; now its own target's owner — or anyone else — reaps it directly.

GetAction

func GetAction(actionID string) string

GetAction returns a formatted summary of a single pending or executed action. Cancelled/vetoed/expired actions are reaped — their history is in emitted events.

GetPending

func GetPending() string

GetPending returns the IDs of all pending, non-expired actions, grouped by target, insertion-ordered within a target. Only targets that actually hold live pendings are visited (round-2 fix Y-1), so the scan cannot be inflated by registrations alone.

GetTarget

func GetTarget(targetName string) string

GetTarget returns a formatted summary of a registered target.

IsExecuted

func IsExecuted(actionID string) bool

IsExecuted returns true if the action exists and was executed. This is the consumer-side check: combined with target registration it attests that the target's registered owner scheduled the action, it waited at least the registered minimum delay, no guardian vetoed it, and it was executed within its grace window. Executed records are permanent.

IsReady

func IsReady(actionID string) bool

IsReady returns true if the action exists, is pending, its delay has elapsed, and it has not expired.

RegisterTarget

func RegisterTarget(cur realm, name string, minDelay int64, guardian address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as RegisterTarget(cross(cur), ...).

RegisterTarget creates a named target. The caller becomes its owner — the only address that may schedule actions against it. minDelay is the enforced floor for every action's delay. guardian may be empty (no guardian) or an address empowered to veto pending actions.

Render

func Render(path string) string

Render returns a markdown overview. Never panics. Cancelled, vetoed, and expired actions are reaped from state; their history is in events. The page shows the most recent actions only (the ordering index is bounded — fix Y4); older executed records stay queryable via GetAction/IsExecuted forever.

Schedule

func Schedule(cur realm, targetName, data string, delay int64) string

Crossing function. Callable from a transaction via MsgCall, and from another realm as Schedule(cross(cur), ...).

Schedule creates a new timelocked action against a registered target. Only the target's owner may schedule. The delay must be at least the target's registered minimum and at most MaxDelay. Returns the action ID.

SetGuardian

func SetGuardian(cur realm, targetName string, guardian address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SetGuardian(cross(cur), ...).

SetGuardian changes (or clears, with "") the target's guardian. Owner only, and REFUSED while the target has pending actions: the guardian's veto power exists precisely to check the owner during a delay window, so the owner must not be able to strip it mid-window.

KNOWN LIMIT (documented, round-3 audit): the owner can cancel all pending actions, change the guardian, and reschedule — the price is a full fresh MinDelay on every rescheduled action, and every step emits an event (cancellations + the guardian change below), so observers always get MinDelay of warning under the new guardian regime. Guardians protect open windows, not the owner's future.

TransferTargetOwnership

func TransferTargetOwnership(cur realm, targetName string, newOwner address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as TransferTargetOwnership(cross(cur), ...).

TransferTargetOwnership OFFERS a target to a new owner; the nominee must AcceptTargetOwnership to complete it (fix Y5: a one-step transfer let anyone fill a stranger's per-owner quota and dump pending obligations — with an attacker-chosen guardian — on an address that never asked). Owner only. Pass "" to clear a pending offer. Nothing changes hands until the nominee accepts.

Veto

func Veto(cur realm, actionID string) string

Crossing function. Callable from a transaction via MsgCall, and from another realm as Veto(cross(cur), ...).

Veto cancels a pending action as the target's guardian. This is the guardian's whole power: it can stop a scheduled action during the delay window, never create or execute one. The record is reaped; history is the event.


Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.

Dependencies

chain-attested
Importschain, chain/runtime/unsafe, strconv, strings, time
First-party dependenciesnone
Used bynone

Known limitations

curated

The manifest records no limitation for this package. That is an absence of a recorded caveat, not a proof that none exists — the deployment record below is the fuller account, and it always carries its own "what was NOT verified live" section.

Source and records

Source filepearl/r/timelock_guardian/timelock_guardian.gno at commit 6a510c665a53 in the project repository (not public — the digest command above is the check that needs no repository)
Matches the deployed bytesyes — byte-identical
Upstream repositoryhttps://github.com/SillyZir/timelock_guardian
Deployed from commitbb05e64898967125b900a9275ca84229bff5e007
Recordscatalog/applications.md#timelock_guardian
pearl/DEPLOYMENT.md