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-.]+$ # TODO: Improve 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