Join our community of builders on Discord!

On-Chain Permissions & Governance Scope

The Lightchain mainnet is governed entirely on-chain. Every contract that holds protocol state — WorkerRegistry, AIConfig, JobRegistry, Treasury — is owned by a TimelockController that the community drives through LightChainGovernor. Once that handover happened at network launch, the founding team gave up the ability to unilaterally change protocol parameters, whitelist or delist models, move treasury funds, or upgrade contracts. Those actions can only happen through a proposal that the community has voted on and that has cleared the 48-hour Timelock. This page enumerates every privileged surface area on mainnet, who can touch it, and — equally important — what is structurally outside the team's control.

Governance stack at a glance

ComponentPurposeAddress (mainnet)
LightChainGovernorProposal, voting, queueing surface (OpenZeppelin Governor)See Contract Addresses
TimelockControllerOwns every protocol contract; enforces the 48-hour execution delaySee Contract Addresses
NativeVotes (0x…1001)Tracks voting power from native LCAI balances. Provides IVotes to the GovernorGenesis precompile-style address 0x0000000000000000000000000000000000001001
Voting power is denominated in native LCAI held at the proposal snapshot block. No wrapper is needed on Lightchain itself — NativeVotes exposes the IVotes interface directly on top of native LCAI balances. For the configured parameters (proposal threshold, voting delay, voting period, quorum, timelock delay), see Governance & Protocol Parameters.

Timelock roles

The TimelockController is the single account that owns every protocol contract. Access to it is gated by four OpenZeppelin roles:
RoleHolderEffect
PROPOSER_ROLELightChainGovernor onlyOnly the Governor can queue an operation against the Timelock. There is no direct path to queue.
CANCELLER_ROLELightChainGovernor (and an optional Guardian multisig)Can cancel a queued operation before it executes. Cannot create or modify operations.
EXECUTOR_ROLEaddress(0) (open)Anyone can execute a queued operation once the Timelock delay has elapsed. This is intentional — execution is permissionless so no single party can hold a passed proposal hostage.
DEFAULT_ADMIN_ROLENoneRenounced at network launch. After renunciation, role assignments can only be changed by a successful governance proposal that targets the Timelock itself.
The practical consequence is that the only way to mutate any protocol-controlled state is:
  1. A proposer with ≥ PROPOSAL_THRESHOLD LCAI (140,000 LCAI) creates a proposal.
  2. Voting delay (~24 h) elapses; the snapshot block is reached.
  3. Voting period (~14 days) elapses; the proposal must reach quorum (30% of supply) with a majority for.
  4. The Governor queues the operation against the Timelock.
  5. 48 hours pass.
  6. Anyone executes the operation.
There is no path that skips any of these steps. There is no admin key, no upgradable Governor pointing at a different timelock, no proxy admin held off-chain.

What governance controls

Every function below is gated by onlyOwner on a contract whose owner is the TimelockController. The only way to call any of them is the proposal flow described above.

WorkerRegistry — model eligibility and worker stake

FunctionWhat it does
addWhitelistedModel(bytes32 modelId)Adds a model to the per-worker whitelist. Workers can only opt in to whitelisted models.
removeWhitelistedModel(bytes32 modelId)Removes a model from the per-worker whitelist. Already-registered workers lose eligibility for that model.
setGuardian(address) / sunsetGuardian()Manage the Guardian role (see below). sunsetGuardian() is irreversible.
Slashing (slash(address, uint256)) is not governance-controlled — it is onlyJobRegistry, called automatically by JobRegistry when an offense is detected. Governance can tune the slashing rates inside AIConfig, but cannot directly slash a worker.

AIConfig — protocol parameters and model registry

FunctionWhat it does
registerModel(bytes32 modelId, uint256 fee, uint256 maxOutputTokens)Registers a new model and enables it.
enableModel(bytes32 modelId) / disableModel(bytes32 modelId)Toggles the modelEnabled[modelId] flag. Workers cannot serve a disabled model even if it is still whitelisted.
setModelFee(bytes32 modelId, uint256 fee)Updates the per-job fee for a model.
setFeeDistribution(uint16, uint16, uint16)Updates the worker / treasury / fee-pool split. Enforced invariant: shares sum to 10,000 bps.
setMinWorkerStake(uint256)Updates the minimum stake required to register as a worker.
setTimeoutSlashBps, setCompletionTimeoutSlashBps, setDisputeSlashBpsUpdate slashing rates. Each call enforces the rate ≤ maxSlashBps (5,000 bps / 50%).
setDisputeWindow, setResolutionTimeout, setDisputeBondMultiplierUpdate dispute timing and bond sizing.
setDispatcherAddress, setDisputerAddressUpdate the addresses of off-chain services that the protocol authenticates against.

Treasury

FunctionWhat it does
transfer(address, uint256)Sends funds from the Treasury to a recipient.
batchTransfer(address[], uint256[])Sends funds to multiple recipients in one operation.
transferERC20(address, address, uint256)Sends an ERC-20 balance held by the Treasury.
There is no path to drain the Treasury that bypasses a governance proposal. claimWithdrawal() is open (it's the pull-mechanism fallback for a failed transfer), but it can only retrieve funds that were already approved for the caller by a prior governance action.

Contract upgrades

WorkerRegistry, AIConfig, JobRegistry, and Treasury are UUPS proxies. The upgrade authorization function _authorizeUpgrade(address) enforces onlyOwner, and the owner is the Timelock. A contract upgrade requires the same proposal → vote → queue → 48 h → execute flow as any other governance action.

What governance itself cannot do

A successful proposal still cannot move certain dials, because the limits are enforced in the contracts as immutable bounds or invariants:
  • Slashing rates cannot exceed 50%. maxSlashBps = 5000 is a compile-time constant. A proposal that calls setDisputeSlashBps(6000) reverts during execution.
  • Fee distribution must sum to 10,000 bps. A proposal that sets a 50/40/5 split (sum = 9,500) reverts.
  • Dispute window and resolution timeout have minimum bounds. Setters enforce floor values so a proposal cannot collapse the dispute window to zero.
  • JobRegistry.slash is onlyJobRegistry. Governance has no path to slash a worker directly. Slashing only happens through detected on-chain offenses.
These guardrails apply equally to the founding team and to the community — they are properties of the deployed code, not policies enforced off-chain.

The Guardian role

The protocol has a single optional safety surface outside governance: a Guardian address (typically a multisig) that can call pause() on WorkerRegistry and JobRegistry. While paused, the affected contracts reject:
  • New worker registration
  • New session creation
  • New job submission
That is the full scope of Guardian authority. The Guardian cannot:
  • Modify any protocol parameter
  • Add or remove models
  • Move treasury funds
  • Upgrade any contract
  • Cancel an in-flight proposal that does not also hold CANCELLER_ROLE (held by the Governor)
  • Force the system to stay paused indefinitely — only the owner (Timelock, i.e. governance) can unpause(), but governance can also remove the Guardian entirely via setGuardian(address(0)) or sunsetGuardian()
sunsetGuardian() is irreversible: once called, the Guardian role can never be reassigned. This is the path to fully retiring the emergency stop once the network is judged stable.

Model whitelisting and removal — fully decentralized

Of all the governance-controlled surfaces above, model lifecycle is the one the team is most often asked about, so it warrants explicit treatment. There are two layered gates a model passes through to be servable on Lightchain. Both are owned by the Timelock. Neither can be invoked by the founding team alone.

Gate 1 — Protocol-wide registration (AIConfig)

CodeSOLIDITY
Registration sets the canonical fee and output limit and flips the model to enabled. disableModel flips the flag back to false without removing the registration (workers still on the eligible set are immediately rejected by JobRegistry when they try to ack a job for that model).

Gate 2 — Per-worker whitelist (WorkerRegistry)

CodeSOLIDITY
A worker can only register support for a model if it is on this whitelist. Removing a model from the whitelist immediately removes it from every worker's supported-model set, so already-active workers stop receiving jobs for that model.

What this means in practice

  • Adding a new model requires: a proposal targeting both AIConfig.registerModel and WorkerRegistry.addWhitelistedModel (typically batched into one proposal), reaching quorum, and clearing the 48-hour Timelock.
  • Removing a model requires the same proposal flow targeting WorkerRegistry.removeWhitelistedModel and/or AIConfig.disableModel.
  • The core team has no override. There is no admin function, no emergency model-removal path, no off-chain registry to amend. If a proposal to whitelist a model passes, the model is whitelisted. If a proposal to delist passes, the model is gone. The team can vote with its own LCAI like anyone else, but it cannot block, fast-track, or veto.
  • A delisted model can be re-whitelisted later by a subsequent proposal. Delisting is reversible by governance, not permanent.
This is by design. The whitepaper commits Lightchain to community-driven model curation, and the deployed contracts encode that commitment in a way that the team cannot undo without first passing its own governance proposal — which would itself be subject to community vote.