The secrets.json format

The secrets.json file provides confidential values to Effects.

Here’s an example:

{ "staging-aws": (1)
  { "kind": "Secret", (2)
    "data": { ... }, (3)
    "condition": { (4)
      "and": [
        { "isOwner": "..." },
        { "isRepo": "..." },
        "isDefaultBranch"
      ]
    }
  }
}
1 The name of the secret. This is used in the right hand side values of secretsMap.
2 All secrets have kind set to Secret.
3 The confidential data as JSON.
4 A condition field that specifies conditions must hold for the secret to be made available to an Effect.

data

The data field is a free-form JSON object. You can use the fields inside the data object to store secrets that consist of multiple parts. The archetypical example would be:

{ "username": "...",
  "password": "..."
}

In practice, secrets with multiple parts have multiple appearances.

For example, AWS credentials can be represented with "aws_access_key_id" and "aws_secret_access_key" field names.

A TLS client certificate may be represented with "clientCertificate", "clientKey" and "CACertificate". Even if an attribute is not technically confidential, storing it in a secret can be helpful. Storing general configuration in secrets is possible, but suboptimal because it can not (currently) be changed as easily and it can not be used during Nix-sandboxed building and testing.

As a rule of thumb, when an item changes simultaneously with a secret, you could distribute it with the secret. If it changes simultaneously with your software or its configuration, it’s probably best to keep it in source control.

condition

The condition value is written in a small expression language. e, e_1, e_n, etc are metavariables representing subexpressions in the same language; not actual variable references. Similarly s is used to represent any JSON string literal for the purpose of documentation.

The language consists of the following constructs, all of which evaluate to boolean values.

{"and": [e_1, …​, e_n]}: true only when all subexpressons evaluate to true. When the list is empty, it evaluates to true.

{"or": [e_1, …​, e_n]}: true only when any subexpression evalutes to true. When the list is empty, it evaluates to false.

"isDefaultBranch": true only when the Effect is part of a Job that originates from the repository’s default branch; typically the master or main branch. This can be configured by a repository admin in GitHub’s repository settings.

{"isBranch": s}: true only when the Effect is part of a Job that originates from a branch name that is exactly equal to the string s.

"isTag": true only when the Effect is part of a Job that originates from a tag.

{"isRepo": s}: true only when the Effect is part of a Job that originates from a repository whose name is exactly equal to the string s.

{"isOwner": s}: true only when the Effect is part of a Job that originates from a repository owned by an account whose name is exactly equal to the string s. Note that jobs do not currently run for PRs from other owner’s repositories, but this is planned to change.

true: evaluates to true; allow. Equivalent to {"and":[]}.

false: evaluates to false; deny. Equivalent to {"or":[]}.

The lack of a "not" construct isn’t unintentional, because block lists are fundamentally not safe under renames or additions. It could be added nonetheless if the need arises.

Secrets that are used for a single purpose can often be represented by a single "and", whereas secrets that are used in multiple ways may need an "or" of "ands" where each "and" describes one situation where the secret may be used.

Here’s a more complete example of a condition:

{"or": [
  {"and": [
    {"isOwner": "example"},
    {"isRepo": "infra"},
    "isDefaultBranch"
  ]},
  {"and": [
    {"isOwner": "example"},
    {"isRepo": "app"},
    {"or": [
      {"isBranch": "staging"},
      {"isBranch": "dev"}
    ]}
  ]}
]}

In this example, the secret can be used by the default branch of the example/infra repository, and also by the staging and dev branches of the example/app repository.

kind

The kind field is always set to "Secret".