dehub/SPEC.md

196 lines
7.3 KiB
Markdown
Raw Normal View History

# .dehub
The `.dehub` directory contains all meta information related to
decentralized repository management and access control.
## config.yml
The `.dehub/config.yml` file takes the following structure:
```yaml
# accounts defines all accounts which are known to the repo.
accounts:
# Each account is an object with an id and at least one identifier. The id
# must be unique for each account.
- id: some_user_id:
# signifiers describes different methods the account might use to
# identify itself. Generally, these will be different public keys which
# commits will be signed with. At least one is required.
signifiers:
- type: "pgp_public_key"
body: "FULL PGP PUBLIC KEY STRING"
- type: "pgp_public_key_file"
path: ".dehub/some_user_id.asc"
- type: "keybase"
user: "some_keybase_user_id"
# access_controls defines under what conditions different files in the repo may
# be modified. For each file modified in a commit, all access control patterns
# are applied sequentially until one matches, and the associated access control
# conditions are checked. A commit is only allowed if the conditions of all
# modified files are met.
access_controls:
# pattern is a glob pattern describing what files this access control
# applies to. Single star matches all characters except path separators,
# double star matches everything.
- pattern: ".dehub/**"
# signature conditions indicate that a commit must be signed by one or
# more accounts to be allowed.
condition:
type: signature
# account_ids lists all accounts whose signature will count towards
# meeting the condition
account_ids:
- some_user_id
# count describes how many signatures are required. It can be either a
# contrete integer (e.g. 2, meaning any 2 accounts listed by
# account_ids) or a percent.
count: 100%
# This catch-all pattern for the rest of the repo requires that changes to
# any files not under `.dehub/` are signed by at least one of the
# defined accounts.
- pattern: "**"
condition:
type: signature
any_account: true # indicates any account defined in accounts is valid
count: 1
```
# Master commit
All new commits being appended to the HEAD of the `master` branch are subject to
the following requirements:
* Must conform to all requirements defined by the `access_controls` section of
the `config.yml`, as found in the HEAD. If the commit is the initial commit of
the repo then it instead uses the `config.yml` found in itself.
* Must not be a merge commit (this may be amended later, but at present it
simplifies implementation).
* The commit message must conform to the format and semantics defined below.
## Master Commit Message
The commit message for a commit being appended to the HEAD of the `master`
branch must conform to the following format: a single line (the message head)
giving a short description of the change, then two newlines, then a body which
is a yaml formatted string:
```yaml
This is the message head. It will be re-iterated within the yaml body.
# Now the yaml body begins
---
message: >
This is the message head. It will be re-iterated within the yaml body.
The rest of this field is for the message body, which corresponds to the
body of a normal commit message which might give a more long-form
explanation of the commit's changes.
Since the message is used in generating the signature it's necessary for it
to be encoded here fully formed, even though the message head is then
duplicated. Otherwise the exact bytes of the message would be ambiguous.
This situation is ugly, but not unbearable.
# See the Commit Signatures section below for how this is computed. The
# change_hash is always recomputed when verifying a commit, but is reproduced in
# the commit message itself for cases of forward compatibility, e.g. if the
algorithm to compute the hash changes.
change_hash: XXX
# Credentials are the set of credentials which count towards requirements
# specified in the `access_controls` section of the `config.yml` file.
credentials:
- type: pgp_signature
account_id: some_user_id
pub_key_id: XXX
body: "base-64 signature body"
```
## Commit Signatures
When a commit is being signed by a signifier there is an expected data format
for the data to be signed. The format is a SHA-256 hash of the following pieces
of data concatenated together (the "change_hash"):
* A uvarint indicating the number of bytes in the commit message.
* The message.
* A uvarint indicating the number of files changed.
* For each file changed in the commit, ordered lexographically-ascending based
on its full relative path within the repo, the following is then written:
* A uvarint indicating the length of the full relative path of the file
within the repo.
* The full relative path of the file within the repo.
* A little-endian uint32 representing the previous file mode of the file (or 0
if the file is being inserted).
* The 20-byte SHA1 hash of the previous version of the file's contents (or 20
0 bytes if the file is being inserted).
* A little-endian uint32 representing the new file mode of the file (or 0
if the file is being deleted).
* The 20-byte SHA1 hash of the new version of the file's contents (or 20
0 bytes if the file is being deleted).
The raw output from the SHA-256 is then prepended with a `0` byte (for forward
compatibility) and signed, and the result used as the signature body.
# Merge Requests
A merge request (MR) may be pushed to the repository as a new branch at any
time. All MR branch names follow the naming convention `DHMR-short-description`.
An MR branch has the following qualities:
* Meta commits (see sub-section) will only contain a commit message head/body,
but no file changes.
* The most recent substantial commit (as opposed to meta commits) should always
contain the full commit message head and body.
## Meta Commits
Meta commits are those which add information about the changes being requested,
but do not modify the changes themselves.
### Signature Commits
Signature commits sign the changes requested in order to count towards their
access control requirements. The message head of these are arbitrary, but the
body must be formatted as such:
```yaml
# This object matches the one found in the `credentials` section of the master
# commit message.
type: pgp_signature
account_id: some_user_id ```
pub_key_id: XXX
body: "base-64 signature body" # see Commit Signatures sub-section.
```
If a signature commit is added to a MR branch, and a substantial commit is
added after it, then that signature commit will no longer be valid, as it was
only signing the the prior changeset. The signer will need to create and push a
new signature commit, if they agree with the new changes.
## Merging MRs
When an MR has accumulated enough meta commits to fulfuill access control
requirements it may be coalesced into a single commit destined for the master
branch. See the Master Commit Message sub-section for details on how commits in
the master branch must be formatted.
# TODO
* access control patterns related to who may push to MR branches, and what types
of commits they can push.