Encrypted credentials for systemd
For PAT creation, GitHub App registration and minimum GitHub permissions, start with authentication setup . This guide covers storing those credentials for the packaged Linux service. For direct Linux/macOS execution without systemd, use ordinary credential files .
To create a new App and configure its encrypted key in one flow, use
ghalet systemd app setup --scope OWNER/REPO --scale-set project
.
The commands below import existing PATs and App keys.
ghalet systemd credential set registers a named PAT or GitHub App private key
for the packaged ghalet.service. It requires Linux, root, systemd 250 or later,
systemd-creds, and systemctl; --prompt also requires
systemd-ask-password and an interactive terminal. Packages never request
credentials during installation or upgrades, and this command never starts
or restarts the service.
Register a PAT
First copy and edit the package configuration , including the target scopes. Then register a PAT:
sudo ghalet systemd credential set default --promptThe default type is pat. Select exactly one input method; no input method
is chosen implicitly:
sudo ghalet systemd credential set default --file /secure/pat-file
sudo ghalet systemd credential set default --stdin < /secure/pat-file--file requires an owner-only regular file, normally mode 0600, and does
not delete or modify it. Do not put secrets in command arguments, shell
history, YAML values, or environment variables. Prompt cancellation, empty
input, invalid keys and encryption failures leave existing registrations
unchanged. Input is limited to 1 MiB.
Registration encrypts directly from memory with systemd-creds, using
--name=default --with-key=host. Only ciphertext is written to
/etc/ghalet/credentials/default.cred with mode 0600. The host encryption
key is /var/lib/systemd/credential.secret: protect and plan backups of it
along with the encrypted credentials. Encryption does not protect against
root with access to that key, and another host cannot decrypt the ciphertext
without the original key.
The command writes its dedicated drop-in and runs systemctl daemon-reload:
# /etc/systemd/system/ghalet.service.d/credential-default.conf
[Service]
LoadCredentialEncrypted=default:/etc/ghalet/credentials/default.credIt also saves defaults.credential in /var/lib/ghalet/managed.yaml, without
rewriting /etc/ghalet/config.yaml. Static defaults and explicit target
authentication remain authoritative. Select a different configuration with
--config PATH; this does not change the unit’s ExecStart.
See managed setup information
for
path overrides, custom systemd configurations and precedence.
# /var/lib/ghalet/managed.yaml
defaults:
credential:
type: pat
tokenFile: /run/credentials/ghalet.service/defaultOnly targets that omit credential inherit the shared default;
credential: {} selects gh. See credential inheritance
for override rules.
Validate and start explicitly:
sudo ghalet config validate --config /etc/ghalet/config.yaml
sudo systemctl enable --now ghalet
sudo journalctl -u ghalet -fValidation checks configuration, not GitHub authentication or permissions.
Systemd supplies the decrypted credential in a private, read-only runtime
directory at service startup. Standalone processes outside the service do
not automatically receive those runtime files.
ghalet accepts root-owned files under /run/credentials/UNIT/ with a read-only
ACL for the service’s effective UID, provided the parent directories are
root-owned and not writable by other users and no group or unrelated user has
effective access. Ordinary credential files still require owner-only permissions.
Do not chmod systemd’s runtime files: changing the ACL mask can remove the
service user’s access.
GitHub Apps and additional credentials
Import one unencrypted RSA PEM private key (PKCS#1 or PKCS#8):
sudo ghalet systemd credential set app-work-key --type github-app \
--client-id APP_CLIENT_ID --installation-id 12345678 --file /secure/app.pemThis replaces the managed shared default with type: github-app, clientId,
installationId, and privateKeyFile: /run/credentials/ghalet.service/app-work-key.
An existing App default’s client ID is reused if --client-id is omitted.
If --installation-id is omitted, the existing default’s installation ID is
reused only when its client ID matches. Otherwise an installation ID is required.
Installation tokens are acquired by the existing SDK, not entered manually.
Use --no-default to register another secret without modifying managed defaults:
sudo ghalet systemd credential set secondary --prompt --no-defaultSet the selected target’s credential.type: pat and
credential.tokenFile: /run/credentials/ghalet.service/secondary.
App keys use type: github-app, clientId, privateKeyFile, and
installationId together inside credential. Different targets can share a path or
reference different names. Names contain 1-64 ASCII letters, digits,
underscores or hyphens, starting with a letter or digit.
Rotation and rollback
Create the new PAT before revoking the old one, or generate an additional
App key before deleting the old key. For rotation, keep the credential name,
type, and App identity unchanged. Register the new value using --no-default
so configuration stays unchanged:
sudo ghalet systemd credential set default --prompt --no-default
sudo systemctl stop ghalet
# Check the daemon's exit result and logs; continue only after successful shutdown.
sudo systemctl start ghaletApp keys use the same command with --type github-app --file /secure/new.pem
instead of --prompt. The ciphertext is replaced atomically, and the old
ciphertext is retained at /etc/ghalet/credentials/NAME.cred.previous.
Another update is refused until that backup is resolved. Unexpected changes
to the command’s drop-in are not overwritten. Failures during file updates
or daemon reload attempt to restore the prior files and report any rollback
errors explicitly. If file restoration fails, the previous ciphertext backup
is retained and its path is included in the error.
Unlike ordinary credential files reread by apply, encrypted systemd
credentials refresh only on service start. Stop normally and verify successful
shutdown as shown above; resolve forced or incomplete drain
before starting again. All targets sharing the credential switch together.
After verifying authentication and runner operation for every affected target,
revoke the old PAT or remove the old App key, then remove its rollback backup:
sudo rm -- /etc/ghalet/credentials/default.cred.previousIf the new credential fails, keep the old credential valid. Stop normally, verify successful shutdown, restore its encrypted backup, then start again:
sudo systemctl stop ghalet
# Check the daemon's exit result and logs; continue only after successful shutdown.
sudo mv -- /etc/ghalet/credentials/default.cred.previous /etc/ghalet/credentials/default.cred
sudo systemctl start ghaletRollback of a rotation does not change YAML. When changing authentication type or App identity rather than rotating it, separately retain and restore the corresponding configuration. PAT expiration, organization approval and GitHub App private-key rotation still require operator management.