NAME
File::SOPS - Perl implementation of Mozilla SOPS encrypted file format
VERSION
version 0.001
SYNOPSIS
use File::SOPS;
# Encrypt a data structure
my $encrypted = File::SOPS->encrypt(
data => {
database => {
password => 'secret123',
host => 'db.example.com',
},
},
recipients => ['age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p'],
format => 'yaml',
);
# Decrypt
my $data = File::SOPS->decrypt(
encrypted => $encrypted,
identities => ['AGE-SECRET-KEY-1...'],
);
# File operations
File::SOPS->encrypt_file(
input => 'secrets.yaml',
output => 'secrets.enc.yaml',
recipients => ['age1...'],
);
File::SOPS->decrypt_file(
input => 'secrets.enc.yaml',
output => 'secrets.yaml',
identities => ['AGE-SECRET-KEY-1...'],
);
# Extract single value
my $password = File::SOPS->extract(
file => 'secrets.enc.yaml',
path => '["database"]["password"]',
identities => ['AGE-SECRET-KEY-1...'],
);
# Rotate data key
File::SOPS->rotate(
file => 'secrets.enc.yaml',
identities => ['AGE-SECRET-KEY-1...'],
);
DESCRIPTION
File::SOPS is a pure Perl implementation of Mozilla SOPS (Secrets OPerationS), compatible with the reference Go implementation at https://github.com/getsops/sops.
SOPS encrypts values in structured files (YAML, JSON) while keeping keys readable. This enables:
Git-friendly diffs - see which keys changed without decrypting
Partial file inspection without full decryption
Multiple encryption backends (currently age, with PGP/KMS planned)
MAC verification to detect tampering
How SOPS Works
- 1. Generate a random 256-bit data key
- 2. Encrypt the data key for each recipient using age (X25519 + ChaCha20-Poly1305)
- 3. Store encrypted data keys in the
sopsmetadata section - 4. Encrypt each value with AES-256-GCM using the data key
- 5. Compute MAC over the entire structure for tamper detection
Encrypted Value Format
Each encrypted value is stored as:
ENC[AES256_GCM,data:base64==,iv:base64==,tag:base64==,type:str]
File Structure Example
database:
password: ENC[AES256_GCM,data:xyz,iv:abc,tag:def,type:str]
host: ENC[AES256_GCM,data:xyz,iv:abc,tag:def,type:str]
sops:
age:
- recipient: age1ql3z7hjy...
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
<encrypted data key>
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-01-10T12:00:00Z"
mac: ENC[AES256_GCM,data:...,iv:...,tag:...,type:str]
version: 3.7.3
Special Features
unencrypted_suffix - Keys ending with
_unencryptedare not encrypted but included in MACKey rotation - Re-encrypt all values with a new data key via "rotate"
Multiple recipients - Encrypt once, multiple recipients can decrypt
encrypt
my $encrypted = File::SOPS->encrypt(
data => \%data,
recipients => \@age_public_keys,
format => 'yaml', # or 'json', defaults to 'yaml'
);
Encrypts a data structure for specified recipients.
Takes a HashRef in data, encrypts all values (not keys) using AES-256-GCM, and encrypts the data key for each age recipient. Returns serialized encrypted content as a string.
The recipients parameter must be an ArrayRef of age public keys (starting with age1...).
Supported formats: yaml, yml, json.
decrypt
my $data = File::SOPS->decrypt(
encrypted => $encrypted_content,
identities => \@age_secret_keys,
format => 'yaml', # optional, auto-detected
);
Decrypts SOPS-encrypted content.
Takes encrypted content as a string, decrypts the data key using provided age identities, verifies the MAC, and returns the decrypted data structure as a HashRef.
The identities parameter must be an ArrayRef of age secret keys (starting with AGE-SECRET-KEY-1...).
If format is not specified, it will be auto-detected from the content.
Dies if MAC verification fails or if none of the provided identities can decrypt the data key.
encrypt_file
File::SOPS->encrypt_file(
input => 'secrets.yaml',
output => 'secrets.enc.yaml', # optional, defaults to input (in-place)
recipients => \@age_public_keys,
format => 'yaml', # optional, auto-detected from filename
);
Encrypts a file.
Reads the input file, encrypts it for the specified recipients, and writes the encrypted content to the output file. If output is not specified, encrypts in-place (overwrites the input file).
Format is auto-detected from the filename extension (.yaml, .yml, .json) unless explicitly specified.
Returns true on success.
decrypt_file
File::SOPS->decrypt_file(
input => 'secrets.enc.yaml',
output => 'secrets.yaml',
identities => \@age_secret_keys,
format => 'yaml', # optional, auto-detected from filename
);
Decrypts a SOPS-encrypted file.
Reads the encrypted input file, decrypts it using the provided identities, and writes the decrypted content to the output file.
Unlike "encrypt_file", output is required to prevent accidental data loss.
Returns true on success.
extract
my $value = File::SOPS->extract(
file => 'secrets.enc.yaml',
path => '["database"]["password"]',
identities => \@age_secret_keys,
format => 'yaml', # optional, auto-detected from filename
);
Extracts and decrypts a single value from an encrypted file.
This is more efficient than decrypting the entire file when you only need one value.
Path can be specified in two formats:
Bracket notation:
["database"]["password"]Dot notation:
database.password
For array indices, use numeric keys: ["items"][0] or items.0
Returns the decrypted value (scalar, not reference).
rotate
File::SOPS->rotate(
file => 'secrets.enc.yaml',
identities => \@age_secret_keys,
recipients => \@new_recipients, # optional, keeps current recipients
format => 'yaml', # optional, auto-detected from filename
);
Rotates the data key (re-encrypts all values with a new key).
This operation:
- 1. Decrypts the file using
identities - 2. Generates a new random data key
- 3. Re-encrypts all values with the new data key
- 4. Encrypts the new data key for
recipients(or existing recipients if not specified) - 5. Writes back to the same file
Key rotation is recommended periodically for security, or when removing a recipient's access.
Returns true on success.
SEE ALSO
File::SOPS::Encrypted - Encrypted value parsing and generation
File::SOPS::Metadata - SOPS metadata section handling
File::SOPS::Backend::Age - Age encryption backend
Crypt::Age - Perl age encryption implementation
https://github.com/getsops/sops - Reference SOPS implementation in Go
https://age-encryption.org/ - age encryption specification
SUPPORT
IRC
You can reach Getty on irc.perl.org for questions and support.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.