English | Русский
Store selected plugin secrets outside normal Grav config YAML while keeping regular plugin settings in their usual files.
- Loads secrets from private storage files:
- base storage defaults to
user/secrets.yaml - environment storage defaults to
user/secrets.<environment>.yaml
- base storage defaults to
- Overlays those values into Grav runtime config.
- Intercepts Admin saves for protected fields.
- Saves protected fields into private secret files instead of normal plugin config YAML.
- Removes protected keys from plugin config after save.
This is useful when you want to keep:
- normal plugin settings in git
- real secrets outside git
Typical examples:
- OAuth client secrets
- SMTP passwords
- Algolia API keys
Grav already supports configuration overrides through setup.php and server-side environment variables:
There are also plugins built around the environment-variable approach (.env files), for example:
These are valid approaches, but they also have tradeoffs:
- on shared hosting, server-level environment management may be unavailable or inconvenient
.env,setup.php, and server overrides do not integrate with normal Admin and Flex save flows
Secret Split fills that niche: selected plugin secrets are moved out of tracked plugin config YAML into private storage files, while normal Admin editing and save continue to work.
- Grav 1.7.x or 2.x
- PHP 8.1+
- On Grav 2, the Admin Next UI also requires the
apiplugin — the custom field types and the state/migrate/return endpoints are served through it. The storage core works regardless. - The plugin runs the same storage, save-interception, and migration logic on both generations; only the admin UI differs (see below)
bin/gpm install secret-split- Download the ZIP archive of this repository.
- Unpack it into
user/plugins/. - Ensure the final plugin directory is:
user/plugins/secret-split
Base private storage default:
user/secrets.yamlEnvironment override default:
user/secrets.<environment>.yamlExamples:
user/secrets.yamluser/secrets.localhost.yamluser/secrets.example.com.yaml
Both paths are configurable via Secret Split plugin settings:
base_storage_fileenvironment_storage_pattern
Behavior is intentionally close to Grav config layering:
- values coming from base plugin config are written to base
user/secrets.yaml - values coming from current env plugin config are written to
user/secrets.<environment>.yaml - returning values back to config restores them into the matching base or current env config layer
- secret files are created again automatically when the corresponding base or current env layer writes back into secrets
- if Grav environment name is empty / undefined, env-specific storage is disabled instead of creating
secrets.unknown.yaml
Secret Split's own plugin configuration lives in:
user/config/plugins/secret-split.yamlThat file stores the protected field list and plugin options.
Actual protected values live in the configured private storage files, which by default are user/secrets.yaml and user/secrets.<environment>.yaml.
The plugin configuration lets you define protected fields grouped by plugin:
- choose a plugin
- choose one or more fields from that plugin
secret-splititself is intentionally excluded from that list, so the plugin cannot protect its own control fields
Field labels are collected from plugin blueprints and shown using their admin-facing names. As soon as a field is selected, the Admin UI also shows its current status and where the value is currently stored.
On Grav 2 (Admin Next) the same marking lives inside the plugin's own settings form: the protected_fields list uses the custom secret-split-field type (field select filtered by the row's plugin plus a live status chip), and the secret-split-overview field renders the status tiles and the Move to secrets / Move to config actions in the same place admin1 injected them. An optional Sidebar menu entry option (admin_sidebar_item, enabled by default) puts a Secret Split shortcut in the left admin sidebar that links straight to this settings page; disable it to keep only Plugins → Secret Split.
When the operator picks an environment in the Admin Next header switcher, Secret Split scopes statuses, saves, and file writes to that environment (base/env secrets files and the matching env/<name>/config/plugins/*.yaml) — mirroring the Grav 1.7 per-hostname layering.
Password-like fields use Grav's field semantics automatically. Empty values for those fields mean:
- empty value means "keep existing secret"
- non-empty value means "replace secret"
This is needed for fields like:
plugins.email.mailer.smtp.password
because password inputs in Grav Admin render empty by design.
For ordinary non-password protected fields, submitting an explicit empty value removes the stored secret automatically.
Removing a field from protected_fields now removes both:
- the field entry from
secret-splitconfig - the stored secret value from
secrets.yaml/secrets.<env>.yaml
The Admin overview supports two deferred actions:
Move to <secrets file>prepares migration into private storageMove to configprepares returning stored values back into tracked plugin config YAML
After clicking either action, the Admin page immediately updates the visible field statuses and source labels to preview the future result.
On Grav 1.7 (admin classic) the real file changes still happen only after the normal Admin Save. On Grav 2 (Admin Next) the overview actions apply immediately — the SPA has no deferred form-save to attach them to.
After Move to config followed by Save (Grav 1.7) or the Return to config action (Grav 2):
- current values from the selected secrets file are written back into plugin config YAML
- the corresponding entries are removed from
user/secrets.yaml/user/secrets.<env>.yaml - field status changes from
StoredtoPending - the source label switches to the config YAML file
Fully supported:
- normal plugin config save flow
- Flex configure flow, including
algolia-pro
The Admin page uses live preview for field state, but real file changes are still applied only after the normal Admin Save (Grav 1.7; on Grav 2 the overview actions apply immediately).
debug_loggingenables verbose plugin-side logging for save, migrate, and Flex flows- environment-specific storage is used only when Grav resolves a non-empty environment name
- secrets files are written with
0600permissions; multi-file operations are serialized throughuser/.secret-split.lock
Internal Grav/Admin/Flex integration details, workarounds, and current limits are documented here:
MIT (see LICENSE).
