# Acceptance criteria for the Obnam client

## 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.

### 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.

~~~scenario
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.\n"
~~~

### 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`.

~~~scenario
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.

~~~scenario
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.

~~~scenario
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.

~~~scenario
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"
~~~

### 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.

~~~scenario
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.\n"
~~~

### 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.

~~~scenario
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": ""
~~~

## 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.

### 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.

~~~scenario
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 repo init
when I run obnam chunk encrypt --key secret --label sticky.tape greeting.txt --id-file id.txt
when I run obnam chunk decrypt --key secret --id-file id.txt
then stdout is exactly "Hello, world.\n"
~~~

### 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.

~~~scenario
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 repo init
when I run obnam chunk encrypt --compress --key secret --label sticky.tape greeting.txt
when I run obnam repo find sticky.tape --one-match-output=id.txt
when I run obnam chunk decrypt --key secret --id-file id.txt
then stdout is exactly "Hello, world.\n"
~~~

### 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.

~~~scenario
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 repo init
when I run obnam chunk encrypt --label sticky.tape --key secret greeting.txt --id-file=id.txt

when I run obnam chunk inspect --id-file id.txt
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-file id.txt
then stdout is valid JSON
then stdout contains ""id":"
then stdout contains ""label":"
then stdout contains ""data": ""
~~~

## Local backup repository

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

### Initialize a backup repository

_Want:_ The user can check if a directory has been initialized as a
backup repository, and initialize it if not.

_Why:_ It seems reasonable to require a backup repository to be initialized
before it's used, to prevent accidental misuse.

~~~scenario
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 repo is
then command fails

when I create directory mychunks
when I try to run obnam repo is
then command fails

when I run obnam repo init
when I run obnam repo is
then exit code is 0
~~~


### Add a chunk to the backup repository

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

_Why:_ This is fundamental to using a backup repository.

~~~scenario
given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
given a directory mychunks

when I run obnam repo init
when I run obnam chunk encrypt --key secret --label data greeting.txt
~~~

### Find chunks using labels

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

_Why:_ This is fundamental to de-duplication.

~~~scenario
given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
given a directory mychunks

when I run obnam repo init
when I run obnam chunk encrypt --key secret --label data-chunk greeting.txt
when I run obnam chunk encrypt --key secret --label data-chunk  greeting.txt
when I run obnam chunk encrypt --key secret --label client-chunk greeting.txt

when I run obnam repo find missing
then stdout is exactly ""

when I run obnam repo find data-chunk
then stdout contains 2 lines

when I run obnam repo find client-chunk
then stdout contains 1 lines
~~~

### Remove a chunk from backup repository

_Want:_ The user can remove a chunk from the backup repository.

_Why:_ This is fundamental to being able to remove backups.

~~~scenario
given an installed obnam
given file .config/obnam/config.yaml from config.yaml
given file greeting.txt
given a directory mychunks

when I run obnam repo init
when I run obnam chunk encrypt --key secret --label data-chunk --id-file id.txt greeting.txt
when I run obnam repo remove --id-file id.txt
then stdout doesn't contain "xyzzy"
~~~

## Client chunk

In this chapter we verify that we can manage client chunks using
chunks in the backup repository.

### Initialize a client chunk

_Want:_ The user can create a client chunk.

_Why:_ This is fundamental for having a client chunk.

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

### 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.

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

### 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.

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

### 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.

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

### 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.

~~~scenario
given an installed obnam
given file .config/obnam/config.yaml from config.yaml
when I create directory mychunks
when I run obnam repo init
when I run obnam --client-key my.secret client init --client-name 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 --client-name my.host
then stdout contains "my.key"
~~~

### 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.

~~~scenario
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 repo init
when I run obnam --client-key my.secret client init --client-name 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.\n"
~~~

### 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.

~~~scenario
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 repo init
when I run obnam --client-key my.secret client init --client-name 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": ""
~~~

## 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.

~~~{#alice.key .file  .tx}
-----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-----
~~~

### 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.

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

### 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.

~~~scenario
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\n"
~~~

~~~{#hello.txt .file .txt}
hello, world
~~~

## Credentials

In this chapter we verify that we can manage credential chunks in the
backup repository.

### Create an OpenPGP software key credential

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

_Why:_ This is an important credential.

~~~scenario
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 repo 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 ""
~~~

~~~{#sop-generate .file .sh}
##!/bin/bash

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

## Convenient use of Obnam

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

### Convenient configuration file

A configuration file for convenient use of `obnam`.

~~~{#comfy.yaml .file .yaml}
client_name: my.host
repository:
  directory: my.repo
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-----
~~~

### Conveniently create a client with an OpenPGP software key credential

_Want:_ It's convenient for the user to initialize a backup repository 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.

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

### Conveniently encrypt and decrypt chunk in a backup repository

_Want:_ It's convenient for the user to encrypt data as a chunk in the backup repository,
and to decrypt that chunk.

_Why:_ We want Obnam to be nice to use.

~~~scenario
given an installed obnam
given file .config/obnam/config.yaml from comfy.yaml
given file message.txt
when I create directory my.repo
when I run obnam repo init
when I run obnam client init --credential softy
when I run obnam chunk encrypt --label data --key-name default --id-file id.txt message.txt
when I run obnam chunk decrypt --key-name default --id-file id.txt --output out.dat
then files message.txt and out.dat match
~~~

~~~{#message.txt .file .txt}
hello there, dear friend
~~~


## Use Obnam server for chunk storage

This section we verify the `obnam remote` subcommands work with the Obnam
server to manage chunks in server storage.

The client configuration needs to be created for each scenario in this section,
as it'll contain the API token. The following script generates the token and
the client configuration file.

~~~{#create-token.sh .file .sh}
#!/usr/bin/env bash
set -xeuo pipefail

obnam-server --config server-config.yaml token --output token.txt --allow "$1"
cat <<EOF > .config/obnam/config.yaml
repository:
  url: http://localhost:8000
  token: "$(cat token.txt)"
server_url: http://localhost:8000
server_token: "$(cat token.txt)"
EOF
~~~

### Manage chunks on the server using API

_Want:_ The Obnam client can store a chunk on the Obnam server and later
retrieve it by ID, or delete it, using the server API directly.

_Why:_ This verifies the client can use the API directly.

~~~scenario
given an installed obnam
given file server-config.yaml
given file greeting.txt
given file .config/obnam/config.yaml from comfy.yaml
given executable file create-token.sh

when I run obnam-server --config server-config.yaml init
when I run ./create-token.sh delete
when I run, as a daemon, obnam-server --config server-config.yaml serve

when I run obnam chunk encrypt --key secret --label first greeting.txt --output chunk.file
when I run obnam api put OVsgIaDAmIOEq1hSn_Zauw test.chunk --filename chunk.file
when I run obnam api get OVsgIaDAmIOEq1hSn_Zauw --filename returned.chunk
then files chunk.file and returned.chunk match

when I run obnam api find test.chunk
then stdout is exactly "OVsgIaDAmIOEq1hSn_Zauw\n"

when I run obnam api delete OVsgIaDAmIOEq1hSn_Zauw
when I run obnam api find test.chunk
then stdout is exactly ""
~~~

## Manage chunks on the server using client internal abstraction

_Want:_ The Obnam client can store a chunk on the Obnam server and later
retrieve it by ID, or delete it, using the internal repository abstraction
in the client only.

_Why:_ This make sure the client can handle local and remote repositories in
the same way and that the user only needs update the configuration.

~~~scenario
given an installed obnam
given file server-config.yaml
given file greeting.txt
given file .config/obnam/config.yaml from comfy.yaml
given executable file create-token.sh

when I run obnam-server --config server-config.yaml init
when I run ./create-token.sh delete
when I run, as a daemon, obnam-server --config server-config.yaml serve

when I run obnam chunk encrypt --key secret --label first greeting.txt --id-file id.txt
when I run obnam chunk decrypt --key secret --id-file id.txt --output decrypted.chunk
then files greeting.txt and decrypted.chunk match
~~~


# FIXME

See [Subplot issue #419](https://gitlab.com/subplot/subplot/-/work_items/419). This top level
chapter needs to be here so that the document structure in the generated HTML is correct. Once
the bug is fixed in Subplot and a release with the fix is made, this chapter can be removed.
