gno.security
The Immune realm is not on gno.land yet. When it is deployed at gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune, these pages fill in by themselves.

For owners

Ask for your code to be checked

Immune reviews only code whose owner asked for it. Asking takes a few minutes, and none of it is secret: your consent, your terms and every coin are on the record on gno.land.

You will need a gno.land account in Adena (version 1.22.2 or later) or gnokey, a little GNOT for fees, and the age tool to read what volunteers send you.

1. Show that the code is yours

Pick the way that fits your code. In the lines below, g1youraddress stands for your gno.land address; connect Adena and it is filled in.

A file in your repository

For a git repository on codeberg.org, github.com, gitlab.com.

  1. Register it with the form below. You get a target id, such as t7.
  2. Commit a file named .gnosh-immune at the root of your default branch, holding this line with your target id:
    gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune t7 g1youraddress
  3. That is your consent. Every volunteer's gnosh checks the file on the tip of your default branch, and that the revision under review is in that branch's history, before it claims a piece. Take the line out and new work stops.

Forks and mirrors are refused, and a commit that exists only in a pull request does not count.

One line in your domain's security.txt

No commit needed, if your project already says where to report security issues.

  1. Add one line to https://example.com/.well-known/security.txt, naming the repositories it covers (with none listed, it covers any you register):
    Immune: gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune g1youraddress https://github.com/example/project
  2. Your repository's SECURITY.md (at its root, in .github/ or in docs/) must name your domain, as the host of a link or of an email address. Most projects already have one:
    # Security
    
    Please report vulnerabilities to security@example.com.
  3. Register it with the second form below: the domain, the repository, and the pin hash of your security.txt. That is the sha256 of its Contact, Encryption, Canonical, Policy, Preferred-Languages and Immune lines, each written name: value in lower case with single spaces, sorted, one per line. Expires and comments are left out, so renewing the file does not change it. Pick a saved copy of the file in the form and your browser works it out, or, for a file without a PGP signature:
    curl -sfL https://example.com/.well-known/security.txt | tr -d '\r' |
      awk 'NR == 1 { sub(/^\357\273\277/, "") }
           { i = index($0, ":"); if (!i) next
             n = tolower(substr($0, 1, i - 1)); gsub(/^[ \t]+|[ \t]+$/, "", n)
             if (n !~ /^(contact|encryption|canonical|policy|preferred-languages|immune)$/) next
             v = substr($0, i + 1); gsub(/[ \t\f\v]+/, " ", v); gsub(/^ | $/, "", v)
             print n ": " v }' |
      LC_ALL=C sort | awk 'NR > 1 { printf "\n" } { printf "%s", $0 }' | sha256sum

Why both sides. A domain is not a repository. Whoever controls example.com could otherwise sign up someone else's code and have its findings sealed to them. The SECURITY.md is the repository's own people pointing at your domain; look-alike hosts and bare mentions do not count.

The file must be valid by RFC 9116: served over HTTPS from the domain itself, with a Contact, an Expires in the future, and, if it has Canonical lines, one naming the address it is fetched from. A whole file, for example:

Contact: mailto:security@example.com
Expires: 2027-11-01T00:00:00Z
Canonical: https://example.com/.well-known/security.txt
Immune: gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune g1youraddress https://github.com/example/project

gno.security fetches it every minute and shows on your target's page whether it still names you and whether its contact and opt-in lines are still the ones you pinned. Renewing Expires changes nothing; when you change a contact, pin the file again there.

A Gno package on gno.land

Register it as kind gno with its path, such as gno.land/r/g1youraddress/app. Its namespace must be your address or a name registered to you; the realm checks that itself.

Proposed for you by an admin

If an admin attested your code for you after you agreed, it waits for you on its target page. Accept it there with your own key.

Register

Register your code

Makes you the owner of a new target and gives you its id. For a repository, then commit the .gnosh-immune file with that id. For a Gno package, its namespace must be your address or a name registered to you.

https://<host>/<owner>/<repo> on a listed host (codeberg.org, github.com, gitlab.com), or gno.land/r/<namespace>/... for a Gno package.
Optional: coins sent with the call back the target as your own share.

Or sign it with gnokey
gnokey maketx call -pkgpath gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune -func Register -args <kind> -args <where it is> -send <back it now with in ugnot>ugnot -gas-fee 25000ugnot -gas-wanted 25000000 -broadcast -chainid onyx-1 -remote https://rpc.onyx.testnets.gno.land:443 <your key name>

Register by your domain's security.txt

No commit needed: your domain's security.txt names you, and the repository's SECURITY.md names your domain. Pin the security.txt's contact and opt-in lines as they are now.

The domain that serves /.well-known/security.txt over HTTPS. No scheme, no path.
Pick a saved copy of the file below, or work it out with the command in step 3 above.

Or sign it with gnokey
gnokey maketx call -pkgpath gno.land/r/g14ssyf3yvlq3usd2z03uzgmx65vl47y8d8rk7fg/immune -func RegisterSite -args <domain> -args <repository> -args <pin hash of the security.txt> -send <back it now with in ugnot>ugnot -gas-fee 25000ugnot -gas-wanted 25000000 -broadcast -chainid onyx-1 -remote https://rpc.onyx.testnets.gno.land:443 <your key name>

2. Set your terms

On your target's page. They are what you promise volunteers, published before anyone starts:

Seal key
Your age public key. Reports are sealed to it, and only you can read them until you disclose. Make one and keep the private half safe:
age-keygen -o owner.key
age-keygen -y owner.key    # prints age1..., your seal key
Reviews per piece
1 to 3 volunteers check each piece, independently.
Stipend
Paid for every review you do not reject, holes or not. An honest "nothing found here" is work too.
Bond
What a volunteer puts up per claim. It comes back with an accepted review and goes to the commons with a rejected one, so junk costs its sender.
Bounties
What you pay for a hole by how bad it is: low, medium, high, critical, rising.
Embargo
7 to 90 days after a round closes, for you to fix and disclose. Nothing is opened before.
Record needed
How many accepted reviews a volunteer needs before they may claim your code.
Models
You may ask volunteers to use models they run themselves, so no model provider sees your code's holes.

Setting terms is your signed agreement to the realm's safe harbor:

The owner authorizes good-faith security review of the registered code, at the revisions it lists in rounds, by volunteers following these terms: source review and local testing only, no access to any running system, findings sealed to the owner until the round is disclosed or its embargo ends. The owner will not pursue legal action against volunteers acting within these terms.

3. Fund it

Anyone may back your target, you too, from its page; a new backer gives at least 1 GNOT. You are just another backer: there is no way for backers' money to reach you. Each backer can take back their own unspent share whenever no round is open or waiting for its verdicts.

4. Open a round

On your target's page: name one revision (a full commit hash) and the tip of your default branch that holds your consent, and list the pieces to review, one path per line, up to 200, each with a hint for the volunteer if you like. Set a bounty reserve of at least one critical bounty. The round locks pieces × reviews × stipend, plus the reserve, from the pot.

5. Read, fix, disclose

  1. Reports arrive sealed to your key. Each report's page has its sealed text; read it with your owner key:
    age -d -i owner.key sealed.age | tail -c +5 | gunzip
    Each report holds its finding, a fix as a patch, a test that fails before the fix and passes after, and the report's own key (an AGE-SECRET-KEY line).
  2. Close the round when you have enough, or it closes after 30 days. Your embargo starts then.
  3. Before your deadline, disclose the round on its page: one line per report with its verdict, its severity and its key. Every report opens to everyone and the pot pays: accepted reports get the stipend, the bounty and the bond back; duplicates the stipend and the bond back; rejected ones nothing, with their key published so anyone can see what was rejected.

If you stay silent past your deadline, finders may open their own reports and are paid the stipend; thirty days later anyone may lapse the round, and the finders who opened share your bounty reserve. Your record shows rounds disclosed on time, late and never.