Obnam 3 acceptance criteria

By: Lars Wirzenius

2025-10-19 07:11

Table of Contents

1 Introduction

"Obnam 3" is a project to explore the implementation of the fundamental parts of backup software. It's not an actual backup program, but it may some day grow into that.

This is a "subplot" document, which explains the acceptance criteria for Obnam, and how to verify Obnam meets them. This document is meant to be processed by Subplot to produce an HTML page and a test program.

At the moment this document is aimed at those develping Obnam. It should be updated to be suitable for all stakeholders, not just developers.

1.1 Stakeholders

This document is meant to express the needs and wants ("acceptance criteria") of people who want to (eventually) use Obnam for their backups. To be manageable, the stakeholders are named here. Other people's opinions also matter, but for this document, the following people have committed to actively participate in maintaining the acceptance criteria and how to verify they are met.

Note that names here can be pseudonums. Legal identities are not necessary. What's needed is some way know who is relevant for each criterion.

If you'd like to be a stakeholder for Obnam, start participating in discussions and help maintain this document, and commit to doing that for the time being.

2 Shared test files between verification scenarios

This chapter contains embedded files used in various verification scenarios.

2.1 Simple configuration file

store: mychunks

2.2 Configuration file with credential

This uses an OpenPGP key generated for this document. It is not used for anything else. Using a pre-generated key for scenarios is a little easier than generating a new one every time.

store: mychunks
credentials:
  softy:
    openpgp-key: |
        -----BEGIN PGP PRIVATE KEY BLOCK-----
        
        xVgEaL0m1BYJKwYBBAHaRw8BAQdAndFYVrDLfsB700KGlV7/FzhhJGfaBGxJ/b74
        rEp2D4YAAQDoQe1n5dEFMG8qdeRox7uz7LEbAycdX71ttACeuP2plhDGzRFhbGlj
        ZUBleGFtcGxlLmNvbcKaBBAWCABCBQJovSbUFiEEJvYHN5AuPh2CWk3vxETPZ1iE
        BxMCGwMCHgEECwkIBwYVDgoJDAgBFg0nCQIIAgcCCQEIAQcBAhkBAAoJEMREz2dY
        hAcT8QQA/2WovW8z5+XAUHS+4yGaifjbC9FLrBjtJk3tEIMDrx31AQD0ccWW1TMN
        L5NvM8e34Oc+NKrgFi71NNmjNp2HmpjcD8ddBGi9JtQSCisGAQQBl1UBBQEBB0Bf
        7mppbi9TUSe5BUD+wIkUhNaTVCDByDBEFb0FWQeTbAMBCAcAAP94T+3o2bZsSKpi
        7pADJ8UX/rmlxDWCpZlmE9USy35WgBICwngEGBYIACAFAmi9JtQCGwwWIQQm9gc3
        kC4+HYJaTe/ERM9nWIQHEwAKCRDERM9nWIQHE3zYAQDSEA78GTuVt3UNyp9SLWr6
        5HN827RT0itIK0Q13sbIIQD/Z9jiMgHQ8BswgYJJSgvzOBoKouNRLeYZvMSRqWaE
        8Qk=
        =3qj+
        -----END PGP PRIVATE KEY BLOCK-----

2.3 Simple plaintext file

Hello, world.

3 Acceptance criteria for individual chunks on disk

In this chapter we cover acceptance criteria for dealing with individual chunks stored by themselves on disk. This means referring to chunks by file name, and providing encryption keys on the command line. The concern for this chapter is to make sure we can create and handle individual encrypted chunks only, not collections of them.

3.1 Chunk encrypt/decrypt round trip via file

Want: The command obnam chunk encrypt encrypts a message, and obnam chunk decrypts decrypts the message, giving the original message.

Why: This ensures the encryption can be undone.

Who: Lars.

given an installed obnam
given file greeting.txt
when I run obnam chunk encrypt --key secret --label sticky.tape greeting.txt --output chunk.file
when I run obnam chunk decrypt --key secret chunk.file
then stdout is exactly "Hello, world. "

3.2 Logging level can be set

Want: The Obnam user can set the logging level, and it's info if not set.

Why: This is an important usability issue when troubleshooting.

Who: Lars.

Helpfully, obnam logs its version at every log level when it starts. In fact, it does that so that we can verify the different ways to set the logging level work.

The default level is info.

given an installed obnam
when I run obnam config
then stderr doesn't contain "TRACE"
then stderr doesn't contain "DEBUG"
then stderr contains "INFO"
then stderr contains "WARN"
then stderr contains "ERROR"

We can set the log level with the OBNAM_LOG environment variable.

when I run env OBNAM_LOG=error obnam config
then stderr doesn't contain "TRACE"
then stderr doesn't contain "DEBUG"
then stderr doesn't contain "INFO"
then stderr doesn't contain "WARN"
then stderr contains "ERROR"

We can set the log level with the --log-level global option.

when I run obnam --log-level=error config
then stderr doesn't contain "TRACE"
then stderr doesn't contain "DEBUG"
then stderr doesn't contain "INFO"
then stderr doesn't contain "WARN"
then stderr contains "ERROR"

The option overrides the environment variable.

when I run env OBNAM_LOG=error obnam --log-level=trace config
then stderr contains "TRACE"
then stderr contains "DEBUG"
then stderr contains "INFO"
then stderr contains "WARN"
then stderr contains "ERROR"

3.3 Chunk encrypt/decrypt with compression round trip via file

Want: The command obnam chunk encrypt compresses and encrypts a message, and obnam chunk decrypts decrypts the message, giving the original message.

Why: This ensures chunks can be compressed.

Who: Lars.

given an installed obnam
given file greeting.txt
when I run obnam chunk encrypt --compress --key secret --label sticky.tape greeting.txt --output chunk.file
when I run obnam chunk decrypt --key secret chunk.file
then stdout is exactly "Hello, world. "

3.4 Inspect an encoded chunk in file

Want: The command obnam chunk inspect should decode an encrypted chunk and output the contained information as JSON.

Why: This is a useful debugging tool.

given an installed obnam
given file greeting.txt
when I run obnam chunk encrypt --label sticky.tape --key secret greeting.txt --output chunk.file
when I run obnam chunk inspect --filename chunk.file
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": null"
when I run obnam chunk inspect --filename chunk.file --key secret
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": ""

4 Acceptance criteria for individual chunks in repository

In this chapter we cover acceptance criteria for dealing with chunks in a repository. This means referring to chunks by id.

4.1 Chunk encrypt/decrypt round trip via repository

Want: The command obnam chunk encrypt encrypts a message, and obnam chunk decrypts decrypts the message, giving the original message.

Why: This ensures the encryption can be undone.

Who: Lars.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
when I create directory mychunks
when I try to run obnam store init
when I run obnam chunk encrypt --key secret --label sticky.tape greeting.txt --id=chunk0
when I run obnam store list
when I run obnam chunk decrypt --key secret --id chunk0
then stdout is exactly "Hello, world. "

4.2 Chunk encrypt/decrypt with compression round trip via repository

Want: The command obnam chunk encrypt compresses and encrypts a message, and obnam chunk decrypts decrypts the message, giving the original message.

Why: This ensures the encryption can be undone.

Who: Lars.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
when I create directory mychunks
when I try to run obnam store init
when I run obnam chunk encrypt --compress --key secret --label sticky.tape greeting.txt --id=chunk0
when I run obnam store list
when I run obnam chunk decrypt --key secret --id chunk0
then stdout is exactly "Hello, world. "

4.3 Inspect an encoded chunk in repository

Want: The command obnam chunk inspect should decode an encrypted chunk and output the contained information as JSON.

Why: This is a useful debugging tool.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
when I create directory mychunks
when I try to run obnam store init
when I run obnam chunk encrypt --label sticky.tape --key secret greeting.txt --id=chunk0
when I run obnam chunk inspect --id chunk0
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": null"
when I run obnam chunk inspect --key secret --id chunk0
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": ""

5 Acceptance criteria for local chunk store

In this chapter we concentrate on storing individual chunks in a chunk store on the local file system.

We may later add chunk verification when adding it to the store, but for now it's just a blob. It is, in any case, not possible for the repository to fully verify a chunk without the encryption key.

This is a fake chunk blob.

5.1 Initialize a store

Want: The user can check if a directory has been initialized as a chunk store, and initialize it if not.

Why: It seems reasonable to require a chunk store to be initialized before it's used, to prevent accidental misuse.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
then directory mychunks does not exist
when I try to run obnam store is
then command fails
when I create directory mychunks
when I try to run obnam store is
then command fails
when I run obnam store init
when I run obnam store is
then exit code is 0

5.2 Add a chunk to the store.

Want: The user can add a chunk to the store, and assign it and ID and label. The store will treat the chunk as an opaque blob of data.

Why: This is fundamental to using a chunk store.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file chunk.blob
given a directory mychunks
when I run obnam store init
when I run obnam store add xyzzy data-chunk chunk.blob
when I run obnam store list
then stdout is exactly "xyzzy "

5.3 Find chunks using labels

Want: The user can find chunks that have a specific label.

Why: This is fundamental to de-duplication.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file chunk.blob
given a directory mychunks
when I run obnam store init
when I run obnam store add xyzzy data-chunk chunk.blob
when I run obnam store add plugh data-chunk chunk.blob
when I run obnam store add advent client-chunk chunk.blob
when I run obnam store find missing
then stdout is exactly ""
when I run obnam store find data-chunk
then stdout contains "xyzzy"
then stdout contains "plugh"
then stdout doesn't contain "advent"
when I run obnam store find client-chunk
then stdout doesn't contain "xyzzy"
then stdout doesn't contain "plugh"
then stdout contains "advent"

5.4 Get path to chunk in store

Want: The user can find out the path where a chunk is stored.

Why: This enables the user to use the obnam chunk commands.

Note that we do not want to know what the actual basename of the chunk file is. That would couple this test too tightly with the chunk store implementation. Instead, we verify that the chunk file exists.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file chunk.blob
given a directory mychunks
when I run obnam store init
when I run obnam store add xyzzy data-chunk chunk.blob
when I run obnam store path xyzzy -o chunk.filename
then a file exists whose name is in chunk.filename

5.5 Remove a chunk from store

Want: The user can remove a chunk from the store.

Why: This is fundamental to being able to remove backups.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file chunk.blob
given a directory mychunks
when I run obnam store init
when I run obnam store add xyzzy data-chunk chunk.blob
when I run obnam store remove xyzzy
when I run obnam store list
then stdout doesn't contain "xyzzy"

6 Acceptance criteria for client chunk

In this chapter we verify that we can manage client chunks using chunks in the chunk store.

6.1 Initialize a client chunk

Want: The user can create a client chunk.

Why: This is fundamental for having a client chunk.

given an installed obnam
given file .config/obnam/config.yaml from config-with-credential.yaml
when I create directory mychunks
when I run obnam store init
when I run obnam client init my.host
then command is successful

6.2 Avoid client chunks with the same name

Want: The user can't easily create a client chunk with the same name as an existing client chunk.

Why: This avoids confusing a user.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
then command is successful
when I try to run obnam --client-key my.secret client init my.host
then command fails

6.3 List clients

Want: The user can list all the clients using the came client key.

Why: This is necessary so the user can find their client chunk.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
when I run obnam --client-key my.secret client list
then stdout is exactly "my.host "

6.4 Show client

Want: The user can see the contents of a client chunk.

Why: This is necessary so the user can manage their client chunk.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
when I run obnam --client-key my.secret client show my.host
then stdout is valid JSON

6.5 Generate new key

Want: The user can generate a new key to add to their client chunk.

Why: This is necessary so that keys can be added.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
when I run obnam --client-key my.secret client generate my.host my.key
when I run obnam --client-key my.secret client show my.host
then stdout contains "my.key"

6.6 Round trip chunk encryption using key from client chunk

Want: The command obnam chunk encrypt encrypts a message, and obnam chunk decrypts decrypts the message, giving the original message, using a key from the client chunk.

Why: This ensures the encryption can be undone and that keys in the client chunk can be used.

Who: Lars.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
when I run obnam --client-key my.secret client generate my.host my.key
when I run obnam --client-key my.secret chunk encrypt --client-name my.host --key-name my.key --label sticky.tape greeting.txt --output chunk.file
when I run obnam --client-key my.secret chunk decrypt --client-name my.host --key-name my.key chunk.file
then stdout is exactly "Hello, world. "

6.7 Inspect an encrypted chunk using key from client chunk

Want: The command obnam chunk inspect should decode an encrypted chunk and output the contained information as JSON, using a key from the client chunk.

Why: This is a useful debugging tool.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
when I create directory mychunks
when I run obnam store init
when I run obnam --client-key my.secret client init my.host
when I run obnam --client-key my.secret client generate my.host my.key
when I run obnam --client-key my.secret chunk encrypt --client-name my.host --key-name my.key --label sticky.tape greeting.txt --output chunk.file
when I run obnam --client-key my.secret chunk inspect --filename chunk.file --client-name my.host --key-name my.key
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": ""

7 Acceptance criteria for SOP use

In this chapter we verify that the subcommands for using SOP work.

The scenarios in this chapter assume the rsop implementation of SOP is installed. The following key is used for tests.

-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: F1B5 3CE4 D58F 5EE0 DB49  E93D D308 DBA8 F346 9D87
Comment: alice@example.com

xVgEaGouURYJKwYBBAHaRw8BAQdAwmubq+MJ9L1mFLeu/tbBuZcd62xB7SHo8svW
oP8YzCYAAQDtW8IaAE3KUZyRicnJT60CeyX1HSjHEdLMakiVjwEqkg62wsALBB8W
CgB9BYJoai5RAwsJBwkQ0wjbqPNGnYdHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu
c2VxdW9pYS1wZ3Aub3JnCuRn7KA8qBwlhsLzKeLYR6LkJD39n+mUjZ7LvH5XyZQD
FQoIApsBAh4JFiEE8bU85NWPXuDbSek90wjbqPNGnYcAAJO6AP4smjs8y0uoAgFv
1QHCZSGqIXzb5/ulRehHiaPrR96SkQD/RTQbANI4VmcTLwRCoZuc3XSThuY88xhN
UlRjdUKwYAfNEWFsaWNlQGV4YW1wbGUuY29twsAOBBMWCgCABYJoai5RAwsJBwkQ
0wjbqPNGnYdHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn
BmG3slqmL6kmKRbbSzOiJ0YkE57gsUEhIPBGCtAmrLUDFQoIApkBApsBAh4JFiEE
8bU85NWPXuDbSek90wjbqPNGnYcAAJ5SAQDucdeO2v1hMrcNIZcoKDF2aTXaV5ow
8kQXRyxYaWNu7wEA8iJhkRfXpbNSjto9oVJbcNwbM+MRTETTvmBv8H1NtAjHWARo
ai5RFgkrBgEEAdpHDwEBB0CYa0AlsbUsX5ns0U+h3yRIwsiEG4lKveaeVkFX7vad
NAABANUKKpmiLekMiuMQhcuXGlJMlSJqQkIB/1T6IKgCRPrODkzCwL8EGBYKATEF
gmhqLlEJENMI26jzRp2HRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEt
cGdwLm9yZzPbR/OqiSoa/P193hNyHRFsqMST1L+THnXgkY4Ecm6yApsCvqAEGRYK
AG8FgmhqLlEJEFEgM+e3gPjeRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
aWEtcGdwLm9yZ8QuOIqBX8CO7e/J5B+gMA3K8L6jXYv5Gp7F4PasjP50FiEE540/
jO2RcE120CXFUSAz57eA+N4AAApJAQCVQqmu0Ie3Ml9974GcX9gIsHsE7w4WzvHu
ffo+GJbKBAD7BzZBoIYgUCzNOfE3FHAYF/Y6Fy5/yv3e9/MLWlEIJQAWIQTxtTzk
1Y9e4NtJ6T3TCNuo80adhwAAC18BAIfEO0tMDrOFwL9JYn00krQ+Jg4+IgjAWwV7
TNcF/VJ/AQDGztlGw6BOZ+lhMv4M6JOpQ164lEuEPcvfrQLevcr+AcddBGhqLlES
CisGAQQBl1UBBQEBB0DtnK67P9j3vrlqqtU3RAdbamAPGIV0w/DT82SLRFQfagMB
CAcAAP9sCiZjyF57cKFR9SSaiDxwY9Whu5XWW6UjoKhs9bAK4BFNwsAABBgWCgBy
BYJoai5RCRDTCNuo80adh0cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh
LXBncC5vcmdrX/23/Yv+SBhKS8r2ow2AIOo/uJK08QNqxVrWgiY9LgKbDBYhBPG1
POTVj17g20npPdMI26jzRp2HAABjpQD/VP24eyMAHIANX5E4OYsFm+S3ThEI3yDA
lmfm/ze6sw0BAJVIZgd9sUySCg/RkckXxHf3ruPma7uPaN+aSCxSXqoF
=teYP
-----END PGP PRIVATE KEY BLOCK-----

7.1 Extracting a certificate from a key

Want: User can extract an OpenPGP certificate from an OpenPGP key with obnam.

Why: This is useful for testing and development of OpenPGP credentials.

given an installed obnam
given file alice.key
when I run obnam sop extract-cert rsop alice.key
then stdout isn't exactly ""

7.2 Encryption round trip

Want: User can encrypt and decrypt date using a SOP implementation via obnam.

Why: This is useful for testing and development of OpenPGP credentials.

given an installed obnam
given file alice.key
given file hello.txt
when I run obnam sop encrypt rsop alice.key hello.txt -o encrypted
when I run ls -l encrypted
when I run cat encrypted
when I run obnam sop decrypt rsop alice.key encrypted
then stdout is exactly "hello, world "
hello, world

8 Acceptance criteria for credentials

In this chapter we verify that we can manage credential chunks in the chunk store.

8.1 Create an OpenPGP software key credential

Want: The user can create a credential chunk using OpenPGP software keys.

Why: This is an important credential.

given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file sop-generate
when I run bash sop-generate alice.tsk
when I create directory mychunks
when I run obnam store init
when I run obnam credential list
then stdout is exactly ""
when I run obnam --client-key my.secret credential openpgp-soft alice.tsk
when I run obnam credential list --all
then stdout isn't exactly ""
#!/bin/bash

set -euo pipefail
rsop generate-key alice@example.com > alice.key > "$1"

9 Acceptance criteria for convenient use of Obnam

This chapter collects acceptance criteria and scenarios for using the obnam command line tool in a convenient way.

9.1 Convenient configuration file

A configuration file for convenient use of obnam.

store: chunk.store
credentials:
  softy:
    openpgp-key: |
        -----BEGIN PGP PRIVATE KEY BLOCK-----
        
        xVgEaL0m1BYJKwYBBAHaRw8BAQdAndFYVrDLfsB700KGlV7/FzhhJGfaBGxJ/b74
        rEp2D4YAAQDoQe1n5dEFMG8qdeRox7uz7LEbAycdX71ttACeuP2plhDGzRFhbGlj
        ZUBleGFtcGxlLmNvbcKaBBAWCABCBQJovSbUFiEEJvYHN5AuPh2CWk3vxETPZ1iE
        BxMCGwMCHgEECwkIBwYVDgoJDAgBFg0nCQIIAgcCCQEIAQcBAhkBAAoJEMREz2dY
        hAcT8QQA/2WovW8z5+XAUHS+4yGaifjbC9FLrBjtJk3tEIMDrx31AQD0ccWW1TMN
        L5NvM8e34Oc+NKrgFi71NNmjNp2HmpjcD8ddBGi9JtQSCisGAQQBl1UBBQEBB0Bf
        7mppbi9TUSe5BUD+wIkUhNaTVCDByDBEFb0FWQeTbAMBCAcAAP94T+3o2bZsSKpi
        7pADJ8UX/rmlxDWCpZlmE9USy35WgBICwngEGBYIACAFAmi9JtQCGwwWIQQm9gc3
        kC4+HYJaTe/ERM9nWIQHEwAKCRDERM9nWIQHE3zYAQDSEA78GTuVt3UNyp9SLWr6
        5HN827RT0itIK0Q13sbIIQD/Z9jiMgHQ8BswgYJJSgvzOBoKouNRLeYZvMSRqWaE
        8Qk=
        =3qj+
        -----END PGP PRIVATE KEY BLOCK-----

9.2 Conveniently create a client with an OpenPGP software key credential

Want: It's convenient for the user to initialize a chunk store and create a client there.

Why: We want Obnam to be nice to use.

This assume the configuration file has already been created and that means the OpenPGP software key has been generated. It'll probably be a good idea for obnam to help with that, later.

given an installed obnam
given file .config/obnam/config.yaml from comfy.yaml
when I create directory chunk.store
when I run obnam store init
when I run obnam client init testy --credential softy
when I run obnam client list
then stdout isn't exactly ""

9.3 Conveniently encrypt and decrypt chunk in a store

Want: It's convenient for the user to encrypt data as a chunk in the store, and to decrypt that chunk.

Why: We want Obnam to be nice to use.

given an installed obnam
given file .config/obnam/config.yaml from comfy.yaml
given file message.txt
when I create directory chunk.store
when I run obnam store init
when I run obnam client init --credential softy testy
when I run obnam chunk encrypt --label data --client-name testy --key-name default --id test.chunk message.txt
when I run obnam chunk decrypt --client-name testy --key-name default --id test.chunk --output out.dat
then files message.txt and out.dat match
hello there, dear friend