Help us shape the future of Go! Take the 2026 Go Developer Survey

atrest

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 9 Imported by: 1

README

atrest

A small Go library that seals local files, such as token caches, with the operating system's own data protection, so a copy of the file taken off the account that wrote it cannot be read. A backup, a synced home directory and a disk image all carry the file without the means to open it.

It exists so that a program which has to keep a credential on disk does not also keep a key beside it. A key stored next to the ciphertext protects nothing, and the operating system already holds a secret tied to the user's logon that the program can borrow.

Install

go get github.com/excelano/atrest

Usage

import "github.com/excelano/atrest"

stored, err := atrest.Seal("myapp/token", cache)
if err != nil { /* ... */ }
// write stored to disk

plain, sealed, err := atrest.Open("myapp/token", stored)
switch {
case errors.Is(err, atrest.ErrCannotOpen):
    // sealed by another user or machine: treat the file as absent
case err != nil:
    // ...
case !sealed && atrest.Available():
    // plaintext from an older build: use it, and store it sealed now
}

The name passed to Seal has to be passed again to Open, and data sealed under one name does not open under another. Make it specific to the program and the file, and never change it: a new name makes every existing file unreadable.

The envelope

Seal returns a JSON object naming the envelope version and the algorithm that sealed the data. Open accepts either that envelope or plaintext and reports which it was given, so a program can migrate its existing files on first read instead of discarding them.

The envelope is JSON so that an older build of a program, one that hands the file straight to a JSON parser, sees an object it does not recognise rather than bytes it cannot parse. A parser that tolerates unknown fields reads the envelope as an empty document and carries on, and if that older build then saves its own data back around the envelope's fields, Open recognises the mixture as plaintext and removes the stale fields. Mixed old and new builds sharing a file therefore cost a fresh start in the old build, not a failure.

Platforms

On Windows, Seal uses DPAPI, which encrypts under a key derived from the user's logon credentials, and passes the name as DPAPI's entropy.

On Linux and macOS, Seal encrypts with AES-256-GCM under a key it keeps in the platform's own secret store rather than beside the ciphertext: the user's D-Bus Secret Service on Linux, falling back to the kernel's per-user keyring when no session bus or no unlocked collection is reachable, and the login Keychain on macOS. A key that only lives in the kernel keyring does not survive a reboot, which costs one sign-in and nothing else, since the cache Seal protects is disposable.

Where nothing is reachable — no D-Bus session and no usable keyring, a locked Keychain, a container whose seccomp profile blocks the kernel keyring call — Seal returns its input unchanged rather than failing, so the caller's file stays plaintext and its protection is whatever file mode the caller gives it. This can happen even though Available reports true, since availability is a property of the platform and reachability is a property of the moment.

Sealing protects a file that leaves its context. It does not protect a file from other programs running as the same user, on any platform, because the operating system opens sealed data for any of them.

License

MIT.

Documentation

Overview

Package atrest protects small local files, such as token caches, with the operating system's own data-protection facility, so their contents are useless once copied off the account that wrote them: into a backup, a synced home directory, or a disk image.

Seal wraps data in a JSON envelope naming the algorithm that protected it. Open reverses Seal and also accepts plaintext, reporting which it was given, so a caller that finds plaintext written by an older build can read it and seal it again instead of discarding it. Where no facility exists, and where one exists but nothing on the machine can reach it right now, such as a container with no D-Bus session and no usable kernel keyring, Seal returns data unchanged; Available reports whether the platform has a facility at all, not whether this call will succeed.

The envelope is JSON so that an older build of a program which hands the file to a JSON parser sees an object it does not recognise, rather than bytes it cannot parse. MSAL's token cache, for one, reads an unrecognised object as an empty cache and prompts for sign-in, where unparseable bytes fail every call.

Protection is against the file leaving its context. Any process running as the same user can open what Seal produced, on every platform.

Index

Constants

This section is empty.

Variables

View Source
var ErrCannotOpen = errors.New("atrest: sealed data cannot be opened here")

ErrCannotOpen reports sealed data that this build cannot open: sealed on another machine or by another user, sealed by an algorithm this platform lacks, or written by a newer envelope version. The data is not corrupt, but nothing here will read it; a cache in this state should be treated as empty.

Functions

func Available

func Available() bool

Available reports whether this platform has a sealing facility at all. It is true on Linux and macOS even where nothing is reachable at the moment — no D-Bus session, no usable kernel keyring, no Keychain — since that can change between one call and the next; Seal itself is what falls back to plaintext on a call it cannot seal.

func Open

func Open(name string, data []byte) (plain []byte, sealed bool, err error)

Open returns the plaintext of data and whether data was sealed. Data that is not an envelope is returned as plaintext with sealed false; a caller that can seal should then store it sealed.

A JSON object carrying envelope fields alongside others is also plaintext: an older build that preserves unknown fields read the envelope as an empty document and saved its own contents back around the envelope's fields. Those fields are removed before the plaintext is returned.

An envelope that cannot be opened here returns an error wrapping ErrCannotOpen.

func Seal

func Seal(name string, data []byte) ([]byte, error)

Seal protects data under name and returns the envelope to store. name must be the same at every Open of the result, and should be specific to the program and the file, since it keeps one program's sealed data from opening under another's name.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL