What the GitHub Actions connector is

The connector writes an environment’s keys into a repository’s Actions secrets, so a workflow can read them with ${{ secrets.DATABASE_URL }} without anybody pasting a value into a settings page.

It runs when you change something. Save a key in a connected environment and the push is owed from that moment; a worker performs it a few seconds later.

One app, one repository per project, one environment each

A GitHub deployment environment holds the secrets one deployment uses, which is what a kyvlt.sh environment holds — so those two are what line up, and the rest is said once, above them:

  • The organisation creates a GitHub App and installs it where kyvlt.sh should write. Nobody pastes a credential, and the repositories it may reach are chosen in GitHub by whoever owns them.
  • A project is aligned to a repository, once. Every environment under it inherits that.
  • An environment names the deployment environment its keys land in, and whether all of them travel or only some collections.

kyvlt.sh pushes, because there is nowhere in GitHub to ask from

The Kubernetes connector runs beside your workloads and asks kyvlt.sh for what it needs, which is why kyvlt.sh holds no credential for your cluster. GitHub has no equivalent place to put such a thing. The only in-repository option would be a workflow holding a kyvlt.sh token — stored as a GitHub secret, which is the thing you came here to configure.

So this one goes the other way: kyvlt.sh writes. That is a real difference in what you are trusting, and it is the reason it goes through an app that asks for five permissions and no more — GitHub shows you that list before you agree, and the repositories it applies to are the ones you pick. None of them is read access to your code.

Secrets and variables

GitHub has two places and kyvlt.sh writes to both. A secret is encrypted, write-only and masked in workflow logs. A variable is plain text, readable by anybody who can read the repository, and printed in logs as itself.

A key in kyvlt.sh is a secret unless somebody says otherwise. Marking one non-sensitive changes nothing about how kyvlt.sh holds it — still sealed, still audited, still concealed on screen — and changes which tab receives it.

It is worth doing for anything that is not a credential. A region pushed as a secret makes a workflow print Region: ***, and a secret whose value is true masks that word everywhere in the log.

What kyvlt.sh asks GitHub for, and why

Five permissions. GitHub shows you this list before you agree, so what follows is the reasoning rather than the claim — the claim is GitHub’s.

PermissionWhat it allowsWhy it is needed
Secrets — Read and writeCreating and replacing Actions secrets, and listing their names and timestamps. It does not allow reading a secret's value: GitHub returns that to nobody.Writing them is the feature. The listing half is how kyvlt.sh reports what is in GitHub and notices that somebody edited a secret there.
Variables — Read and writeThe same for Actions variables, which are plain text — so reading one does return its value.Keys marked non-sensitive travel as variables. Being able to read them back is why kyvlt.sh can say a variable matches, which it can never say of a secret.
Environments — Read and writeCreating and replacing the secrets and variables of a named deployment environment. Not its protection rules, not its deployment history.An environment secret is a different thing from a repository secret, gated by that environment's rules, and this is the permission that writes one. Without it an environment connection pushes nothing and reports a 403.
Actions — Read-onlyListing a repository's deployment environments. It also allows reading workflow runs, job logs, artifacts and runner configuration, because GitHub files all of those under one permission and offers no narrower one.It is asked for one thing: so that connecting an environment is a list to choose from rather than a name to type. A misspelled environment is a push that lands nowhere and reports success, and nothing else can prevent it — the endpoint that names a repository's environments is filed under Actions, not Environments. kyvlt.sh reads nothing else this allows. Decline it and the connector still works: you type the environment name instead of picking it.
Metadata — Read-onlyRepository names, topics, collaborator lists. Every GitHub App has it and it cannot be declined.Mandatory. kyvlt.sh uses it to list the repositories an installation was granted, so aligning a project is a choice rather than a spelling test.

What it deliberately does not ask for

  • Contents. kyvlt.sh cannot read a line of your code, and an integration that writes secrets has no business being able to.
  • Actions. It cannot see workflow runs, logs or artifacts — which is where a secret would be visible if a workflow printed one.
  • Issues, pull requests, administration, packages, pages. None of it is needed to write a secret.

There is one consequence worth knowing. kyvlt.sh offers to list a repository’s deployment environments so you can pick one instead of typing it, and it asks GitHub for that list with the permissions above. If GitHub declines, the field becomes a text box rather than a dropdown — which is the trade for not asking for a wider permission to save a few keystrokes.

And which repositories

The permissions say what kyvlt.sh may do; the installation says where. When you connect, GitHub asks which account and which repositories — Only select repositories is the right answer almost always — and kyvlt.sh can reach nothing outside that list. Changing it later is the same screen, in GitHub, and kyvlt.sh finds out the next time it looks.

GitHub never tells anyone what a secret is

This shapes everything else on these pages. The GitHub API will list a secret’s name, when it was created, and when it was last updated. It will not return the value, to kyvlt.sh or to you or to anybody else.

Two consequences follow, and neither is a limitation of the connector:

  • Nothing can say the two sides match. kyvlt.sh knows what it last wrote and when; it cannot compare that against what is there now, so it never claims to.
  • There is no reconciliation. Rather than reading a value to decide whether a write is needed, kyvlt.sh writes again. A push is cheap and a wrong assumption is not.

What it will not do

  • It never deletes. Remove a key from an environment and the secret stays in GitHub, reported as unmanaged. Deleting it is a decision about somebody else’s repository, and a workflow depending on it would fail for a reason nothing on that screen explains.
  • It never reads from GitHub into kyvlt.sh. Values travel one way, out.
  • It refuses GITHUB_-prefixed names, which GitHub reserves. You are told when you connect rather than after a push half-fails.