What would show up as met
Each check in the automated score, what it looks at and what a project can publish for it to read as met. This is what the check looks for. It makes no promise about a score or grade, and claiming or paying never changes a score. Many checks only read public pages, so a change shows up at the next automated run, not at once. How scores are made →
Who can change the program
- If the program is final, revoke the upgrade authority. It then reads as immutable.
- Otherwise move the authority to a multisig or a DAO. For a multisig, use a threshold of at least 2, add a time lock, and do not keep a separate config authority that can change members without a vote.
- Publish the multisig or DAO address on your own website so the page and the on-chain account can be tied together.
How we check: We read the program’s upgrade authority on Solana (public RPC) and recognise Squads, Serum-style multisigs and SPL Governance accounts.
Who can mint the token
- If no more tokens will ever be minted, revoke the mint authority. It then reads as met.
- If minting must stay possible, move the authority to a multisig (threshold 2 or more, time lock) or a DAO and publish that address on your site.
How we check: We read the mint account on Solana (public RPC).
Who can freeze token accounts
- If freezing is not needed, revoke the freeze authority.
- If it is needed, move it to a multisig (threshold 2 or more, time lock) or a DAO and publish that address on your site.
How we check: We read the mint account on Solana (public RPC).
Can anyone rebuild the program from the source?
- Build the program reproducibly from your public source with the Solana verifiable-build tooling (
solana-verify). - Submit the build to the registry so that it appears in the OtterSec registry (verify.osec.io). The check reads that registry; it does not rebuild anything itself.
How we check: We ask the OtterSec build registry (verify.osec.io, a third party) whether the deployed program matches a build from the public source. We report what the registry says; we do not rebuild or vouch for anything ourselves. Any registry entry that matches the program’s current on-chain hash counts, not only the registry’s default entry (method 0.2.5).
Audit reports
- Link each audit report from an official page: your docs, your website or an audits folder in your GitHub repository.
- Tell us the page through the corrections form so it can be added. The check only confirms that the document is reachable. It does not assess the audit’s quality or whether it covers the current code.
How we check: Audit links come from official pages found for the project, not from a search of the web.
Public source code
- Make the main repository public on GitHub, under the organisation that is listed for the project.
- Link the repository from your website so the two can be tied together.
How we check: We read the repository’s public GitHub API data.
Licence
- Add a standard licence file (for example Apache-2.0 or MIT) that GitHub recognises.
- Custom or source-available terms are shown as partial: we do not judge them.
How we check: We read the licence field GitHub detects.
Recent activity
- A push within the last 90 days reads as met, 91 to 365 days as partial. Nothing to configure: this follows normal maintenance of the public repository.
How we check: We read the date of the last push on GitHub.
security.txt inside the program
- Embed a security.txt block in the program with the
solana-security-txtcrate: name, project_url, contacts and policy. Redeploy to publish it. - This also tells us which programs belong to you, and which are shared programs that do not.
How we check: We read the program account on Solana (public RPC).
Program source is public
- Make the source of each program public and mention its program ID in the repository (for example in the README or the source), so that the ID can be found in public code.
How we check: We look for the program ID in the project’s public repository.
Official address list
- List your official program addresses on a page of your own domain (for example a docs page called “Addresses”). A list only on GitHub reads as partial, and one only on a third-party page as not met.
How we check: We use the pages the project itself publishes.
Time since the last deploy
- Nothing to configure: a deploy more than 30 days ago reads as met, a more recent deploy as partial because the code changed recently.
How we check: We read the last deploy time on Solana.
HTTPS
- Serve your website over HTTPS with a valid certificate.
How we check: We open the homepage over TLS.
Security headers
- Send these four response headers on your homepage: Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options and X-Frame-Options.
- The check looks only at whether each header is present, not how strict it is.
How we check: We read the headers of one homepage request.
Security contact (security.txt)
- Publish a text file at https://your-domain/.well-known/security.txt (or /security.txt) with a
Contact:line, for exampleContact: mailto:security@your-domain. - RFC 9116 also asks for an
Expires:line. We look only forContact:, but addExpires:too so the file stays correct.
How we check: We fetch the two standard locations over HTTPS.
Bug bounty programme
- Link your bug bounty (for example an Immunefi, HackerOne or Cantina page, or a /bug-bounty page of your own) from the homepage, the sitemap or your security.txt.
- “Unknown” here means we found no link. It does not mean you have no programme.
How we check: We look at the homepage links (also as rendered in a browser), sitemap.xml and security.txt, and at Immunefi’s public list for on-chain security.txt names.
Terms and privacy policy
- Link both a Terms page and a Privacy Policy page from the homepage, or list them in sitemap.xml.
- We check that the pages exist. We do not read or assess their content.
How we check: We look at homepage links, sitemap.xml and common paths.
Legal entity named in the terms
- Name the legal entity behind the project in the terms or privacy text, for example “Example Labs Ltd” or “Example Foundation”.
- We do not check a company registry.
How we check: We read the terms and privacy text and look for a company-style name.
Domain age
- Nothing to configure: a domain registered more than a year ago reads as met, 90 days to a year as partial.
How we check: We read the registration date from the public domain registry (RDAP).
Domain, addresses and code point at each other
- Publish your official program addresses on your own domain and link your GitHub organisation from the website, so the three can be tied together.
How we check: We compare what the project’s own pages say with the on-chain addresses and the GitHub organisation.
npm package provenance
- Publish from CI with provenance:
npm publish --provenance.
How we check: We read the npm registry entry of the latest version.
npm install scripts
- Remove preinstall, install and postinstall scripts from the package.
How we check: We read the npm registry entry of the latest version.
npm package points at the source
- Set the
repositoryfield in package.json to your public repository.
How we check: We read the npm registry entry and open the repository link.
API key custody statement
- Publish a manifest that states how keys and transactions are handled (for example unsigned transactions only, no key custody).
How we check: We fetch the manifest the project submitted.
Some checks are shown as “Status only” (for example whether a page is claimed) or “Not built yet”. They never change a score. Something wrong or unclear? Send a correction.