Skip to content
CommonLayer
Documentation menu
On this page

Maintainer handbook

This handbook is for people who maintain a CommonLayer project or want to: how to apply, how assignments work, what you are responsible for, the security deadlines, the periodic tasks, how your monthly eligibility is evaluated and explained, how to appeal, what happens when you step down or are replaced, and how compensation reaches you. It describes the implementation in crates/commonlayer-maintenance and crates/commonlayer-funding. Each step names the portal page and the API operation behind it; the few steps that have no portal page yet say so. The pages were exercised by the Playwright suite against the development seed (locally); no maintainer has used them on a deployed platform.

The short version

  • You maintain a project, not a sum of money. The project's treasury owns the money; you hold an assignment to a slot of its allocation policy.
  • Each month the platform evaluates whether you fulfilled your responsibilities, from facts it records (security deadlines, tasks, releases, reviews). The result is eligible, at_risk, under_review, temporarily_suspended or ineligible, with an explanation in plain language.
  • You are paid for the month if the final state is eligible or at_risk. You are never paid per vulnerability, and nothing is paid automatically: payouts go through a gated, dual-approved payout run.
  • Every decision about you can be appealed, and nobody can decide about themselves.

Becoming a maintainer

  1. Apply on Dashboard → Maintainer → Apply (/dashboard/maintainer/apply, POST /api/v1/maintainers/apply): a public bio and links, and acceptance of the maintenance agreement. The page shows the agreement's full text, version and SHA-256 (GET /api/v1/maintenance/agreement). Edit your profile later on /dashboard/maintainer/profile (GET/PATCH /api/v1/users/me/maintainer).
  2. A platform admin approves or rejects the application on /admin/maintainers (platform.maintainers.approve, a person in an interactive session). Nobody can approve their own application (database CHECK).
  3. Approved maintainers appear in the public directory (GET /api/v1/maintainers) with their public bio, links and assignments. Payout destination and tax status are never public.

Assignments

An assignment binds you to a project in a role:

RoleSlotResponsibilities (default)
primaryprimary (paid)all ten
secondarysecondary (paid)all ten
additional, temporaryas the policy definesas assigned
security_reviewer—security monitoring, acknowledgement, vulnerability triage, remediation coordination

The ten responsibilities are security_monitoring, security_acknowledgement, vulnerability_triage, remediation_coordination, pull_request_review, dependency_updates, pipeline_maintenance, release_publication, artifact_integrity and documentation. Eligibility rules apply only to the responsibilities your assignment holds.

  • A project admin proposes an assignment on the project's Maintainers page (/dashboard/projects/{slug}/maintainers, POST /api/v1/projects/{slug}/assignments, project.maintainers.manage). It becomes active when a platform admin approves it on /admin/maintainers (POST /api/v1/assignments/{id}/approve, interactive session), or immediately if the proposer is a platform admin.
  • The database guarantees one active primary and one active secondary per project, and that no slot is held by two assignments at the same time.
  • Normal funded maintenance needs both a primary and a secondary. Fewer is a maintenance-health degradation that shows on the project (lifecycle maintenance_degraded; unmaintained after 30 days without either), not an error.
  • Assignments start in the current month at the earliest, so settled months never change retroactively.
  • Your active assignment gives you the project role maintainer; a security_reviewer assignment gives the security_reviewer role.

Security duties and deadlines

Findings are opened by the security domain when a scan of a currently supported digest finds a vulnerability that no not_affected statement covers. Each finding has an acknowledgement and a remediation deadline from the project's SLA policy (or the platform default):

Effective severityAcknowledge withinRemediate within
Critical24 hours7 days
High48 hours14 days
Medium7 days30 days
Low, negligible, unknown14 daysend of the calendar month after the month it opened

These are policy defaults, not guarantees. A vulnerability in CISA's KEV catalogue is raised to at least high. A project admin can publish a project SLA version (PUT /api/v1/projects/{slug}/sla); the policy in force when a finding opens is snapshotted on it.

What you do with a finding (project.security.triage), on Dashboard → Maintainer → Findings (/dashboard/maintainer/findings, soonest deadline first) or on the project's security page (/dashboard/projects/{slug}/security):

  • Acknowledge it (POST /api/v1/findings/{id}/acknowledge). Assessing, requesting an exception or starting remediation also acknowledges.
  • Assess applicability: affected, or not_affected with a VEX justification (component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist). For a finding below the confirmation threshold, not_affected closes the finding and is published in the project's OpenVEX document.
  • Second reviewer. For a finding whose effective severity is at or above security.not_affected_confirmation_min_severity (default high, a runtime setting; KEV escalation counts), your not_affected statement is pending: the finding stays acknowledged, its deadlines keep running, the scan gate still blocks, and nothing is published until another person with triage rights on the project confirms or rejects it (pending statements are listed on the project security page and the findings page; POST /api/v1/security/assessments/{id}/confirm or …/reject with a note). You can never confirm your own statement; a service account cannot confirm at all. The same applies to statements imported with an OpenVEX document. A project with a single active maintainer needs a second person with triage rights, for example a security_reviewer assignment (ADR-0037).
  • Request an exception when the deadline cannot be met (no upstream fix, alternative mitigation, coordinated disclosure, external dependency, risk accepted, not applicable) with a justification, an expiry and a new remediation date. Someone else with project.security.exceptions.decide decides it on /dashboard/projects/{slug}/exceptions (a person, never a service account); the database refuses self-decision. The same page lists every exception with its status and decider. While approved, the extended date applies. Exceptions are public with their justification unless embargoed.
  • Remediate by releasing a fixed digest. A finding resolves when every supported version line's current digest has a fresh scan without the vulnerability. The old digest keeps its record forever: a fix never rewrites history.

Breaches are permanent facts. Acknowledging late does not clear the breach; a reopened finding starts a new episode with new deadlines (ADR-0029). Private security reports to your project must be acknowledged within the critical acknowledgement window.

Periodic tasks

maintenance.generate_tasks (daily) creates the required tasks of each period:

TaskCadence
scan_triageweekly
dependency_updatemonthly
pipeline_healthmonthly
docs_reviewquarterly
release_publishwhen upstream is ahead of the latest release of a supported line
pr_reviewper tracked community pull request waiting beyond the review window
security_ackper finding (created when the finding opens; completed when it is acknowledged)

Complete a task with evidence (links, notes, a private flag) on Dashboard → Maintainer → Tasks (/dashboard/maintainer/tasks, POST /api/v1/tasks/{id}/complete); the page also shows your closed tasks. If a task does not apply, request a waiver with a reason on the same page (POST /api/v1/tasks/{id}/waive); a project admin other than you, a person, decides it. Deciding a waiver has no portal page yet: the project admin sends POST /api/v1/tasks/{id}/waive with a decision. Open tasks past their due date are marked missed. The project's public maintenance page (/projects/{slug}/maintenance) lists tasks with their status and the evidence links you did not mark private; waiver reasons, private evidence and security-acknowledgement tasks are never shown there.

How eligibility is evaluated

Once a month, after the month ends, maintenance.evaluate_eligibility computes a preliminary evaluation for every assignment that held at least one day of the month.

Rules

Each rule yields pass, warn, fail or not_applicable with a sentence and references to the evidence. Default thresholds are in maintenance.eligibility configuration.

Rule (responsibility)Fails whenWarns when
sla.acknowledgement (security_acknowledgement)a critical or high finding was acknowledged late or not at allmore than 1 medium or more than 2 low late acknowledgements
sla.remediation (remediation_coordination)a critical or high remediation deadline passed in the month without resolution or approved exceptionthe same for medium (more than 1) or low (more than 2)
sla.outstanding (remediation_coordination)a critical or high finding is overdue at month enda medium or low finding is overdue at month end
reports.acknowledgement (security_acknowledgement)a private security report was not acknowledged in time—
tasks.required (per task kind)2 or more required tasks missed1 missed
release.activity (release_publication)no successful build for more than 45 days while upstream moved, or upstream lag over 30 days in a supported lineupstream lag over 14 days
reviews.responsiveness (pull_request_review)—community pull requests waiting more than 7 days for a first review exceed the tolerance
agreement.accepted—a published agreement is not accepted (the state becomes under_review)

security_ack tasks do not count in tasks.required, because sla.acknowledgement already judges the same deadline.

Time scoping. A deadline, report or task counts only if it fell due while your assignment held the slot. The end-of-month rules (sla.outstanding, release.activity, reviews.responsiveness) apply only if your assignment is still active at month end. If you hand over mid-month, you are not blamed for what happens after; your successor is not blamed for what happened before.

Who wrote the fix does not matter. If a community contributor wrote the fix and you reviewed, released and verified it in time, you met the obligation.

State

Precedence, top to bottom:

  1. An active suspension overlapping the month → temporarily_suspended.
  2. An open appeal, a missing agreement, or an input that could not be loaded → under_review.
  3. Any fail → ineligible.
  4. Any warn → at_risk (still paid; you are notified).
  5. Otherwise eligible.

Explanation

Every evaluation stores a plain-language explanation generated from the rule results, for example "Ineligible for 2026-10: CVE-2026-1234 (critical) was acknowledged 32 h after detection; the limit is 24 h." Hours and days are rounded up, so a limit is never understated. A public variant without private identifiers appears on the project page; the full detail is visible to you and to holders of project.read_private.

The inputs, thresholds and rule-set version are stored with the evaluation, so it can be re-derived exactly later (ADR-0033).

Timeline of a month

  1. After the month ends, preliminary evaluations are stored and you are notified (maintenance.eligibility.decided).
  2. For maintenance.review_window_days (default 2) you can add evidence or complete documentation; evaluations are recomputed when evidence changes.
  3. maintenance.finalize_eligibility (daily) finalizes them. If an input could not be loaded, finalization waits up to seven more days, then finalizes as under_review.
  4. Settlement reads only finalized evaluations. A missing or under_review evaluation results in a held obligation, never a payment and never a forfeiture.

Appeals and overrides

  • Your evaluations, with their explanations and rule results, are on Dashboard → Maintainer → Eligibility (/dashboard/maintainer/eligibility). You may appeal a finalized evaluation within maintenance.appeal_window_days (default 14) from its detail page (/dashboard/maintainer/eligibility/{id}, POST /api/v1/eligibility/{id}/appeals). An open appeal sets the state to under_review.
  • A platform admin with platform.eligibility.override who is not you resolves it on /admin/eligibility, in an interactive session, with a justification of at least 10 characters. Upheld: eligible (or at_risk); denied: the previous state.
  • A platform admin can override a state with a mandatory justification; the computed state is kept next to the override, and overrides are refused while an appeal is open.
  • When a finalized state changes, maintenance.eligibility.changed is published and the funding domain releases or reinstates the money: an upheld appeal after a forfeiture reinstates the amount from the account that received it, as far as that account still holds it, and any shortfall is recorded and shown to administrators.
  • Every decision, override and appeal resolution is audited.

Stepping down and replacement

A project admin ends an assignment (POST /api/v1/assignments/{id}/terminate) or replaces the maintainer (POST /api/v1/assignments/{id}/replace) on the project's Maintainers page, which shows the actual treasury balances next to the statement that a replacement does not touch them:

  1. The old assignment ends at the effective time (replaced or stepped_down).
  2. A new assignment for the same role and slot starts at that time, linked to the old one, pending platform approval unless the actor is a platform admin.
  3. Both are recorded in the assignment history (public), audited, and announced.

The replacement method has no access to the ledger. A test asserts that every balance, transaction and obligation is identical before and after a replacement on a treasury of $10,000; your past obligations and payouts are untouched. The next settlement splits the slot by day between you and your successor (if both are eligible for their days). If the replacement is approved in a later month, the slot is vacant until then.

Platform admins can suspend a maintainer (platform-wide or for one project) and lift the suspension on /admin/maintainers, which also lists suspensions in force and past; nobody can suspend or lift a suspension of themselves.

Compensation

  1. Settlement (after the month, once evaluations are final) creates an obligation for your share of the slot: pending if eligible or at risk, held if under review. See the funding model for how shortfalls, the reserve and hand-overs are handled.
  2. Payout runs pay pending obligations. You need a verified payout destination (a reference at the payment provider; CommonLayer stores no bank details) and a tax status on file, recorded by a platform administrator on /admin/maintainers (PUT /api/v1/admin/maintainers/{user_id}/payout-status). Without them your obligation is skipped and stays owed. The destination is fixed when a payout run is planned.
  3. Real payouts happen only when the operator has configured live payouts and passed every payout gate, including a second platform admin's approval. As of this writing no live payouts are configured anywhere.
  4. Your own obligations and payouts: Dashboard → Maintainer → Compensation (/dashboard/maintainer/compensation, GET /api/v1/users/me/compensation).

Community contributions you review

Contributions (pull requests, reviews, security fixes, documentation) are recorded from tracked GitHub pull requests (merged pull requests by non-bot authors of a linked repository) or submitted by hand. You review them (POST /api/v1/contributions/{id}/review, project.contributions.review, a person, never a service account) and approve or reject with a note; you cannot review your own. Reviewing contributions has no portal page yet; use the API with a session or a personal API token. Approval grants the contributor the contributor role on the project. Reward proposals are made on Dashboard → Maintainer → Proposals (/dashboard/maintainer/proposals) or the project's Rewards page; the pages explain before you click that you can never approve a reward you proposed. Approved contributions are the only basis for a community reward from the flexible pool; see the contributor handbook.

Your dashboard

Dashboard → Maintainer (/dashboard/maintainer, GET /api/v1/users/me/maintainer/dashboard) shows your status, suspensions, the agreement warning, counts of what needs attention, your assigned projects with their three states, and recent evaluations.