openapi: 3.1.0
info:
title: Drawbridge
description: |
# Node
- A node is either a file or a directory, where directory is a collection of [node entries](#section/Node/Entry).
- A node may belong to multiple directory nodes.
- A node in a directory is identified by a case-sensitive name consisting of alphanumeric characters, dashes and underscores and must represent valid URL string, which is unique within a directory.
## Inherent properties
### Contents
All nodes have contents associated with them.
#### Directory node contents
Contents of a directory node is a key-value object with keys representing the node names and values representing [node entries](#section/Node/Entry).
For example:
```
{
"assets": {
"digest": {
"sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg",
"sha384": "DH01Tx3ixOrDYbNcjqw+H53EIFSI232hbotDTy1E/NU6+qodYhdrswrXoEfx/nnx"
},
"from": "myassets:0.1.2"
},
"Enarx.toml": {
"digest": {
"sha256": "DodjLNRr1JB8UWMX622B/g+SGiPHZDAY8hKSiUtHBoE",
"sha384": "mqVuAfXRKap7bdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
}
}
}
```
#### File node contents
Contents of a file node is binary data of an arbitrary [media type](https://www.iana.org/assignments/media-types/media-types.xhtml) other than `"application/vnd.drawbridge.directory.v1+json"`
### Media type
All nodes have a [media type](https://www.iana.org/assignments/media-types/media-types.xhtml) associated with them.
#### Directory node content type
`"application/vnd.drawbridge.directory.v1+json"` type identifies a directory node.
#### File node content type
Any other type than `"application/vnd.drawbridge.directory.v1+json"` identifies a file node.
### Content length
All nodes have a content length associated with them.
#### Directory node content length
Content length of a directory node is equal to byte length of JSON-encoded directory contents without whitespace characters.
#### File node content length
Content length of a file nodes is equal to byte length of raw file contents.
### Content digest
All nodes have a content digest associated with them.
#### Directory node content digest
Content digest of a directory node is equal to hash of lexicographically-sorted JSON-encoded directory contents without whitespace characters.
For example, sha256 content digest of a directory with following contents:
```
{
"assets": {
"digest": {
"sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg",
"sha384": "DH01Tx3ixOrDYbNcjqw+H53EIFSI232hbotDTy1E/NU6+qodYhdrswrXoEfx/nnx"
},
"from": "myassets:0.1.2"
},
"Enarx.toml": {
"digest": {
"sha256": "DodjLNRr1JB8UWMX622B/g+SGiPHZDAY8hKSiUtHBoE",
"sha384": "mqVuAfXRKap7bdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
}
}
}
```
is equal to `Z2oGasnuiV+nldtPLGf+wWDQ14nhUVjbeL8So4Zr1aA`
#### File node content digest
Content digest of a file nodes is equal to hash of raw file contents.
## Entry
Node entry is a combination of exherent properties of the [node](#section/Node) (for example, `from`, representing the source from which it was mirrored, if applicable) and digest of its [inherent properties](#section/Node/Inherent-properties).
For example, `sha384` digest computed over following [inherent properties](#section/Node/Inherent-properties) of a file [node](#section/Node):
```
{
"contentDigest": {
"sha384": "mqVuAfXRKap7bdgcCY5uykM6-R9GqQ8K_uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
},
"contentLength": 42,
"contentType": "text/plain"
}
```
is equal to:
```
7QuaGUAIdLBb9ZzarCY0ybasXdU6QhQzMkXAIiS8UwjDZBLWLNSr4Vhz2Kh+/R9r
```
# Tag
An immutable mapping of a [semver version string](https://semver.org/) to a [node entry](#section/Node/Entry).
# Tree
A [node entry](#section/Node/Entry) identified by a [tag](#section/Tag).
version: 0.1.0
components:
schemas:
SemVer:
description: A [semantic version](https://semver.org/) string.
type: string
pattern: ^[a-zA-Z0-9-.]+$ example: 1.2.3
ContentDigest:
description: Node [content digest](https://www.ietf.org/archive/id/draft-ietf-httpbis-digest-headers-08.html#name-the-content-digest-field).
type: string
pattern: ^\*sha(224|256|384|512)=:[a-zA-Z0-9-_]{38,86}={0,2}:(\*,\*sha(224|256|384|512)=:[a-zA-Z0-9-_]{38,86}={0,2}:)*$
example: 'sha-256=:4REjxQ4yrqUVicfSKYNO/cF9zNj5ANbzgDZt3/h3Qxo=:,\sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrI\iYllu7BNNyealdVLvRwEmTHWXvJwew==:'
ContentLength:
description: Node content length in bytes.
type: integer
format: int64
example: 42
ContentType:
description: Node media type.
type: string
pattern: ^(application|audio|image|text)/[a-zA-Z0-9-_.,+]+$
example: application/vnd.drawbridge.directory.v1+json
Sha224Hash:
description: '[Base64-encoded](https://datatracker.ietf.org/doc/html/rfc4648#section-4) sha224 hash without padding.'
type: string
contentEncoding: base64
minLength: 38
maxLength: 38
example: kKPtnjKyqvTGHEEOuSVCYRnhqdxT1Chq3pmoCQ
Sha256Hash:
description: '[Base64-encoded](https://datatracker.ietf.org/doc/html/rfc4648#section-4) sha256 hash without padding.'
type: string
contentEncoding: base64
minLength: 43
maxLength: 43
example: n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg
Sha384Hash:
description: '[Base64-encoded](https://datatracker.ietf.org/doc/html/rfc4648#section-4) sha384 hash without padding.'
type: string
contentEncoding: base64
minLength: 64
maxLength: 64
example: DH01Tx3ixOrDYbNcjqw+H53EIFSI232hbotDTy1E/NU6+qodYhdrswrXoEfx/nnx
Sha512Hash:
description: '[Base64-encoded](https://datatracker.ietf.org/doc/html/rfc4648#section-4) sha512 hash without padding.'
type: string
contentEncoding: base64
minLength: 86
maxLength: 86
example: Pwpjrc6dKL0MgLLCchb4s9jvDfpOMRzgQ96yrfYtbttYBbxaaM/31ed2dw0tTghK8LAuOmfiUyxhsmToYQrG3g
FileContents:
description: File contents.
example: Hello world!
Entry:
description: A node entry.
type: object
required:
- digest
properties:
digest:
description: Digests of [inherent properties](#section/Node/Inherent-properties) of a [node](#section/Node).
type: object
properties:
sha224:
$ref: '#/components/schemas/Sha224Hash'
sha256:
$ref: '#/components/schemas/Sha256Hash'
sha384:
$ref: '#/components/schemas/Sha384Hash'
sha512:
$ref: '#/components/schemas/Sha512Hash'
additionalProperties: false
DirectoryContents:
description: |
Directory contents.
Keys are node names.
type: object
propertyNames:
pattern: ^[a-zA-Z0-9-_.,]+$
additionalProperties:
$ref: '#/components/schemas/Entry'
example:
assets:
digest:
sha256: n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg
sha384: DH01Tx3ixOrDYbNcjqw+H53EIFSI232hbotDTy1E/NU6+qodYhdrswrXoEfx/nnx
from: myassets:0.1.2
Enarx.toml:
digest:
sha256: DodjLNRr1JB8UWMX622B/g+SGiPHZDAY8hKSiUtHBoE
sha384: mqVuAfXRKap7bdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
headers:
Content-Digest:
required: true
schema:
$ref: '#/components/schemas/ContentDigest'
Content-Length:
required: true
schema:
$ref: '#/components/schemas/ContentLength'
parameters:
Tag:
name: tag
in: path
required: true
schema:
$ref: '#/components/schemas/SemVer'
Content-Digest:
name: Content-Digest
required: true
in: header
schema:
$ref: '#/components/schemas/ContentDigest'
Content-Length:
name: Content-Length
in: header
required: true
schema:
$ref: '#/components/schemas/ContentLength'
Content-Type:
name: Content-Type
in: header
required: true
schema:
$ref: '#/components/schemas/ContentType'
paths:
/_tag:
get:
description: List available tags.
responses:
'200':
description: Available tags sorted lexicographically
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/SemVer'
example:
- 0.5.3
- 1.2.3
- 1.2.4
- 2.0.1-rc1
/_tag/{tag}:
parameters:
- $ref: '#/components/parameters/Tag'
head:
description: Check whether a tag exists.
responses:
'200':
description: Tag exists
'404':
description: Tag does not exist
get:
description: Get a tree node entry associated with a tag.
responses:
'200':
description: Tree node entry associated with the tag
content:
application/json:
schema:
$ref: '#/components/schemas/Entry'
'404':
description: Tag does not exist
put:
description: Create a tag.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Entry'
responses:
'201':
description: Tag associated with a tree node entry
'204':
description: Tag already exists and matches the tree hash
'404':
description: Tree node does not exist
delete:
description: Yank a tag.
responses:
'204':
description: Tag yanked
'404':
description: Tag does not exist
/_tag/{tag}/tree/{path}:
parameters:
- $ref: '#/components/parameters/Tag'
- name: path
in: path
schema:
description: Slash-delimited file path to a node within a tree.
type: string
pattern: ^[a-zA-Z0-9-_.,]+(/[a-zA-Z0-9-_.,])+$
example: foo/bar/baz/file.txt
head:
description: Check whether a tree path exists.
responses:
'200':
description: Tree path exists
'404':
description: Tree or path within it does not exist
get:
description: Get tree path contents.
responses:
'200':
description: Tree path contents
headers:
Content-Digest:
$ref: '#/components/headers/Content-Digest'
Content-Length:
$ref: '#/components/headers/Content-Length'
content:
application/vnd.drawbridge.directory.v1+json:
schema:
$ref: '#/components/schemas/DirectoryContents'
'*/*':
schema:
$ref: '#/components/schemas/FileContents'
'404':
description: Tree or path within it does not exist
put:
description: Upload tree path contents.
parameters:
- $ref: '#/components/parameters/Content-Digest'
- $ref: '#/components/parameters/Content-Length'
- $ref: '#/components/parameters/Content-Type'
requestBody:
content:
application/vnd.drawbridge.directory.v1+json:
schema:
$ref: '#/components/schemas/DirectoryContents'
'*/*':
schema:
$ref: '#/components/schemas/FileContents'
responses:
'201':
description: Tree path uploaded
'204':
description: Tree path already exists and matches uploaded contents
'404':
description: Tree or path within it preceeding the node being uploaded does not exist