# Client Tutorial

## Example in curl

A simple curl example that talks directly to gm-data without any other services should be useful to explain how this works. First, assume that we are *ONLY* running gm-data with no other services or infrastructure.

* gm-data requires a JWT to accept any `POST /write` requests.  We use a tool to create the JWT out-of-band.  The claims in it are similar to what you would have gotten from a JWT server.
* gm-data here is set up as a plaintext service on port 8181, to simplify the explanation.
* The `POST /write` requires that the user is identified.  There are two possibilities:
  * A `USER_DN` header that gm-data uses to go look up a JWT for that user
  * Or, just the actual JWT passed in to the `userpolicy` header.

> Note about security: The `USER_DN` header is *trusted*. We presume that a proxy prevents users from just setting `USER_DN` to any unverified value it wants. The `USER_DN` is not *signed*. A JWT token *is* actually signed. It is signed, and contains identifiers such as `email` and `userDN`. What is necessary is that the JWT contains the things that policy checks against. In this example, it's something in the `values.email` field.

In this example, we are going direct to the plaintext service, and not through the edge server. Because of this, instead of setting the `USER_DN` header, we can pass in an arbitrary JWT into the `userpolicy` header, however we manage to obtain a correctly signed JWT.

The first half creates the JWT, which is just an alternative to passing in `USER_DN`. The second half is the heart of how an upload actually works. Create a JSON object that specifies the minimum fields to describe the upload, and give curl the actual file content to upload with it.

```bash
#!/bin/bash

theFile=$2
theLabel=$1

(
# gmdatatool has the signing key, and gm-data has the verification key.
# JWT lasts for 60000 seconds until it expires.
# It is signed, so it is as valid as if we went to go look it up in the user database.
./gmdatatool jwt-create 60000 <<thejwt
{
  "label": "rob.jonhson",
  "values": {
    "email": [
      "localuser@email.com",
      "rob.jonhson@email.com",
      "rob.jonhson@aol.com",
      "rob.jonhson@deciphernow.com"
    ],
    "age": [
      "adult"
    ],
    "role": [
      "gmdata-admin",
      "gmdata-user"
    ],
    "paidfor": [
       "movies"
    ]
  }
}
thejwt
) > thejwt.txt

thejwt=`cat thejwt.txt`

# Given that we now have a JWT, we can perform an actual file upload.
# In this case, we specify where the file will be uploaded.
# The objectpolicy calculates permissions that a user will have.
# The security just controls what security labels are put on it, as seen in GET /show endpoint.
echo upload ${theFile}
cat <<EOF > metadata.json
[
  {
    "action":"U",
    "parentoid": "1/world/localuser@email.com/pics",
    "name": "${theFile}",
    "isfile": true,
    "security": {
      "label": "${theLabel}",
      "foreground": "white",
      "background": "purple"
    },
    "objectpolicy": {
      "rego": "package policy inOrg{input.claims.values.email[_]==\"localuser@email.com\"}C{inOrg}R{inOrg}U{inOrg}D{inOrg}X{inOrg}P{inOrg}"
    }
  }
]
EOF

time curl -H "userpolicy: ${thejwt}" -X POST -F file=@metadata.json -F file=@${theFile} http://localhost:8181/write
```

> Note that the metadata JSON must come before the actual file being uploaded. There is not a rname field in the metadata (pointing to where an existing blob is), so gm-data takes this to mean that we must upload the blob with the data.
>
> Note that this example uses OpenPolicyAgent's (OPA) Rego language, as of gm-data:1.1.5. You can use both the existing LISP for backwards compatibility, or Rego for policy now.

The `objectpolicy.rego` field or the `originalobjectpolicy` field can now take the standard OPA language as input. The exact same requirements apply to existing policy in this case:

* the input.claims expects a JWT with a `values` field of type `map[string][]string`
* the output should be a write to boolean variables in the set: `["C","R","U","D","X","P"]`
* if `objectpolicy.rego` is specified, then `objectpolicy.requirements` should be left unspecified
* if `originalobjectpolicy` field is the only thing specified, gm-data can compile it for you as long as the language is either the existing LISP language or Rego.

## Explanation

In order to perform any operations in the system, we need claims (from a JWT token) to assert our privilege. All edits to the system come from a multipart mime `POST /write` call. We will start from an empty system, and add data. The root of the filesystem is only editable with root privilege, so we need to make user directories. Given this JWT claim, we need to generate a JWT token from some service (not gm-data) that knows user attributes and is trusted to sign off on them; like short-term certificates for tasks. Currently, the scripts `./makeurlroot`, `./makeurljane` invokes libraries to generate the tokens. You may want to read these short bash scripts to understand it.

```javascript
{
  "label": "asRoot",
  "values": {
    "privilege": [
      "root"
    ]
  }
}
```

The token is generated when we go through a service that makes tokens, like `/services/gm-jwt-security/1.0/token?redir=/services/gm-data/0.1/html/1/&path=/services/README.md`.

![User Object Policy](https://1676458320-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LsNFVozLgvw3NDMzxBg%2Fsync%2F9fc5e696a53e85c0161f36d99552514f1300e208.png?generation=1598636817866825\&alt=media)

> We can host multiple JWT servers in the system. A user passes through a JWT server to have an appropriate cookie set.

It encodes like this valid OAuth Bearer token, that we will use to create root directories.

```bash
eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb290IiwidmFsdWVzIjp7InByaXZpbGVnZSI6WyJyb290Il19fQ.AXckbmNH0HVwuixpLPsj5AJR_cztOkQn0Ltju1klz1MaMu14Keg28R__4sUSSLJYQEKTtE8HfSwcpjnOWFKnW03YAI_XY4l5ZEAo7hjuNA5oQQN1wPPShGrG8tUpFAOSZNuyLJ4SfgHgUW_-Fk1kYhbGDZAkaZL8295wRQkJjvvvvwsT
```

> recognized privilege: root (unrestricted use), readonly (no /write), blobkey (show S3 key)

We will create a directory for <jane.doe@gmail.com> that is readable by everyone (including anonymous), multipart/form-data POST /write with this as first part.

```javascript
{
  "name": "jane",
  "action": "C",
  "parentoid": "0000000000000001",
  "objectpolicy": {
    "label": "forAnonReadjaneFull",
    "requirements": {
      "f": "owner-full-ro-all",
      "a": [
        {
          "v": "email"
        },
        {
          "v": "jane.doe@deciphernow.com"
        }
      ]
    }
  },
  "derived": {},
  "security": {
    "label": "anonymousread ownedby jane.doe@gmail.com",
    "foreground": "#FFFFFF",
    "background": "#000000"
  }
}
```

We get back this:

```javascript
[
  {
    "tstamp": "158340da66e0d624",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asRoot"
    },
    "jwthash": "619c6c4fd8979e1f522990b989737c2f9edb93597f0a80b59e25c33d13a584ef",
    "schemaversion": 6,
    "name": "jane",
    "action": "C",
    "oid": "158340da66d4a444",
    "parentoid": "0000000000000001",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340d597264594",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
```

We will create a directory for <bob.smith@deciphernow.com> that is readable by everyone (including anonymous), multipart/form-data POST /write with this as first part.

```javascript
{
  "name": "bob",
  "action": "C",
  "parentoid": "0000000000000001",
  "objectpolicy": {
    "label": "forAnonReadBobFull",
    "requirements": {
      "f": "owner-full-ro-all",
      "a": [
        {
          "v": "email"
        },
        {
          "v": "bob.smith@deciphernow.com"
        }
      ]
    }
  },
  "derived": {},
  "security": {
    "label": "anonymousread ownedby bob.smith@deciphernow.com",
    "foreground": "#FFFFFF",
    "background": "#000000"
  }
}
```

We get back this:

```javascript
[
  {
    "tstamp": "158340da69ff6a78",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asRoot"
    },
    "jwthash": "619c6c4fd8979e1f522990b989737c2f9edb93597f0a80b59e25c33d13a584ef",
    "schemaversion": 6,
    "name": "bob",
    "action": "C",
    "oid": "158340da69d16150",
    "parentoid": "0000000000000001",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340d597264594",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
```

Now we get a JWT token for jane with these claims.

```javascript
{
  "label": "asjane",
  "values": {
    "age": [
      "driving",
      "drinking"
    ],
    "citizenship": [
      "USA"
    ],
    "email": [
      "jane.doe@deciphernow.com"
    ],
    "name": [
      "jane fielding"
    ],
    "org": [
      "www.deciphernow.com"
    ]
  }
}
```

```bash
eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb2IiLCJ2YWx1ZXMiOnsiYWdlIjpbImRyaXZpbmciLCJkcmlua2luZyJdLCJjaXRpemVuc2hpcCI6WyJVU0EiXSwiZW1haWwiOlsicm9iLmZpZWxkaW5nQGRlY2lwaGVybm93LmNvbSJdLCJuYW1lIjpbInJvYiBmaWVsZGluZyJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSJdfX0.AAMXs9Bm1_BFGpNXJ12MvUlQTpbDbkULedKiU8nVtiTb2E-avEeMF5Yc0phV7bDCvcG8qZgCeeuGIx1AwIX4HmsPANrLm0437R7XWgALLeEoHncMuDh8vfoW3fiIG-edbnHQmUjvHcpvNsW2fHTSvb6GDzf2p9FXhLSGY-z6NA_uyNE9
```

Now we get a JWT token for bob with these claims.

```javascript
{
  "label": "asBob",
  "values": {
    "age": [
      "driving",
      "drinking"
    ],
    "citizenship": [
      "USA"
    ],
    "email": [
      "bob.smith@deciphernow.com"
    ],
    "name": [
      "bob smith"
    ],
    "org": [
      "www.deciphernow.com",
      "ieee"
    ]
  }
}
```

```bash
eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNMdWNhcyIsInZhbHVlcyI6eyJhZ2UiOlsiZHJpdmluZyIsImRyaW5raW5nIl0sImNpdGl6ZW5zaGlwIjpbIlVTQSJdLCJlbWFpbCI6WyJsdWNhcy5tb3RlbkBkZWNpcGhlcm5vdy5jb20iXSwibmFtZSI6WyJsdWNhcyBtb3RlbiJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSIsImllZWUiXX19.AO58W-CnDelUYv0ox8OgluZfrp2K34MVVQnb_WCpMhvFPJQooGDW0rAZO6jXaAVpz-7shE5dilRNPeEzNFddodqqALQH4B-x05ZJsYJwsJakTkbwubufg-_uqUQHc0Bh2hzxKfe9xcsZx3n7OkeOmoOQpuVgaJ228q_8N5tjpZ7WoS7A
```

Create a file in the directory for <jane.doe@gmail.com>. multipart/form-data POST /write, with this in first part, and file blob next part.

```javascript
[
{
  "name": "timer.go",
  "action": "C",
  "parentoid": "158340da66d4a444",
  "objectpolicy": {
    "label": "forAnonReadjaneFull",
    "requirements": {
      "f": "owner-full-ro-all",
      "a": [
        {
          "v": "email"
        },
        {
          "v": "jane.doe@deciphernow.com"
        }
      ]
    }
  },
  "mimetype": "text/plain",
  "derived": {},
  "isfile": true,
  "security": {
    "label": "anonymousread ownedby jane.doe@gmail.com",
    "foreground": "#FFFFFF",
    "background": "#000000"
  }
}
]
```

We get back this.

```javascript
[
  {
    "tstamp": "158340da77f085e0",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "2bf847b0df164363ee63d222a8e32eadce5b940cd9e0dfa578d0e72e00cd05d3",
    "schemaversion": 6,
    "name": "timer.go",
    "action": "C",
    "oid": "158340da77e33a5c",
    "parentoid": "158340da66d4a444",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da66e0d624",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "558/158340da6ed32558"
    },
    "size": 283,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "qMayQ5vYmGJIiGD+b46GhLQQjlKksA3URcS2V0jZXMZaDDmoeq1Esa66OlHWV8+CHhkKIL2EBGWxArq870Sp0E28ygftatK/Wiyp/GJ9gLU8T9U8hHJ/E0s4ERYnlVTjO4X8v79aCBRNV+6xBv7ETr1T3n8ePgHSwEc="
    },
    "encryptednonce": {
      "master": "yUY3tJc/fsGu8JQP"
    }
  }
]
```

Create a file in the directory for <bob.smith@deciphernow.com>. multipart/form-data POST /write, with this in first part, and file blob next part.

```javascript
[
{
  "name": "timer.go",
  "action": "C",
  "parentoid": "158340da69d16150",
  "objectpolicy": {
    "label": "forAnonReadBobFull",
    "requirements": {
      "f": "owner-full-ro-all",
      "a": [
        {
          "v": "email"
        },
        {
          "v": "bob.smith@deciphernow.com"
        }
      ]
    }
  },
  "mimetype": "text/plain",
  "derived": {},
  "isfile": true,
  "security": {
    "label": "anonymousread ownedby bob.smith@deciphernow.com",
    "foreground": "#FFFFFF",
    "background": "#000000"
  }
}
]
```

We get back this.

```javascript
[
  {
    "tstamp": "158340da7db8d66c",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asBob"
    },
    "jwthash": "c752eb6f3618877d076dc1c1dc070d7f77f894c06918f2c5858714659cd6c936",
    "schemaversion": 6,
    "name": "timer.go",
    "action": "C",
    "oid": "158340da7dabda98",
    "parentoid": "158340da69d16150",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da69ff6a78",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9c4/158340da7af5e9c4"
    },
    "size": 283,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "dFrhN4l+h9wTODEpwm7OIsWsf+g10xvZbvjqRw686Mlte8dfU+3x0NValBrqTQfEGolvfmx345mfcvewKNHe5loJcOMtVVdGOsOdE6F5zCaBvuy6UZvbasYRA2aOl262nirMANITttErkueVAA4YEAg6mNvmzd2XzUU="
    },
    "encryptednonce": {
      "master": "U9ub6IEGpNiGvCI5"
    }
  }
]
```

## Write Entire Directories In One Call

In order to do bulk uploads, it is possible to make a single large transaction. This transaction is an array of writes that should succeed or fail together. It is necessary for some writes to refer to previous writes, such as creating a directory in the 5th entry, and the 6th entry declaring that its parent directory is the 5th entry (ie: one entry prior to this one). This means that we need a notion of `references` to copy the `oid` of a previous write (that we won't know until it's inserted into the database) into the `parentoid` of this entry. If we `POST /write` this as `multipart/form-data`, this will be the first part:

```javascript
[
  {
    "name": "bigDir",
    "action": "C",
    "parentoid": "158340da66d4a444",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    }
  },
  {
    "name": "timer.go",
    "action": "C",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "references": [
      {
        "address": -1,
        "from": "oid",
        "to": "parentoid"
      }
    ]
  },
  {
    "name": "testlocal",
    "action": "C",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "references": [
      {
        "address": -2,
        "from": "oid",
        "to": "parentoid"
      }
    ]
  }
]
```

... And the second part will be the actual file:

```go
package client

import "time"

type Timer struct {
    Counter int64
    Start   time.Time
    Elapsed time.Duration
}

func NewTimer() *Timer {
    return &Timer{Start: time.Now()}
}

func (t *Timer) Add(e int64) {
    t.Counter += 3
}

func (t *Timer) Stop() {
    t.Elapsed = time.Since(t.Start)
}
```

The first element of the first part (json) specified a directory. The second element of the first part specified a file. Because in this file we did not specify an Rname for where it goes in S3, it is going to expect that the next part of this multipart is this file.

> Notice the References field used to fill in unknown parentoid with oid of previous directory.

The new directory was assigned an oid of 158340da87cde534, and the two files copied that in as their parent oids. The first file saw it -1 entry back, and second file saw it -2 entries back.

## Update Metadata On An Existing Object (158340da87e468e0) Without Editing File

We will write this update record, where we specify ALL of the state required. We might change anything on it.

```javascript
[
  {
    "name": "timer-renamed.go",
    "action": "U",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    }
  }
]
```

## Delete An Existing Object (158340da87e468e0)

Put into a deleted state. The key field is the oid.

```javascript
[
  {
    "name": "byebye",
    "action": "D",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "octet/stream",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
```

## Reference: Full Bytes Of Calls

Input: Create a directory

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=96671eb25ce299977e9cc5408df31b44c7b7da281debd64fa352f26e8f80
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb290IiwidmFsdWVzIjp7InByaXZpbGVnZSI6WyJyb290Il19fQ.AXckbmNH0HVwuixpLPsj5AJR_cztOkQn0Ltju1klz1MaMu14Keg28R__4sUSSLJYQEKTtE8HfSwcpjnOWFKnW03YAI_XY4l5ZEAo7hjuNA5oQQN1wPPShGrG8tUpFAOSZNuyLJ4SfgHgUW_-Fk1kYhbGDZAkaZL8295wRQkJjvvvvwsT
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--96671eb25ce299977e9cc5408df31b44c7b7da281debd64fa352f26e8f80
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "jane",
    "action": "C",
    "parentoid": "0000000000000001",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--96671eb25ce299977e9cc5408df31b44c7b7da281debd64fa352f26e8f80--
```

Output: The directory is created

```bash
HTTP/1.1 200 OK
Content-Length: 908
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:03 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 32
X-Request-Id: d8e64bc6-a7c9-487b-bd2f-fd45f2609942
X-Schema-Version: 6
X-Session-Id: session158340da653686fc

[
  {
    "tstamp": "158340da66e0d624",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asRoot"
    },
    "jwthash": "619c6c4fd8979e1f522990b989737c2f9edb93597f0a80b59e25c33d13a584ef",
    "schemaversion": 6,
    "name": "jane",
    "action": "C",
    "oid": "158340da66d4a444",
    "parentoid": "0000000000000001",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340d597264594",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
```

Input: Create a directory

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=69fa686e6071bac50921c4ff40c5ea4e61aa6474d23be30acee7362dafd7
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb290IiwidmFsdWVzIjp7InByaXZpbGVnZSI6WyJyb290Il19fQ.AXckbmNH0HVwuixpLPsj5AJR_cztOkQn0Ltju1klz1MaMu14Keg28R__4sUSSLJYQEKTtE8HfSwcpjnOWFKnW03YAI_XY4l5ZEAo7hjuNA5oQQN1wPPShGrG8tUpFAOSZNuyLJ4SfgHgUW_-Fk1kYhbGDZAkaZL8295wRQkJjvvvvwsT
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--69fa686e6071bac50921c4ff40c5ea4e61aa6474d23be30acee7362dafd7
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "bob",
    "action": "C",
    "parentoid": "0000000000000001",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--69fa686e6071bac50921c4ff40c5ea4e61aa6474d23be30acee7362dafd7--
```

Output: The directory is created

```bash
HTTP/1.1 200 OK
Content-Length: 916
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:03 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 34
X-Request-Id: f06319e9-b8ab-4c05-a7ee-0d6283e8090d
X-Schema-Version: 6
X-Session-Id: session158340da685230d4

[
  {
    "tstamp": "158340da69ff6a78",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asRoot"
    },
    "jwthash": "619c6c4fd8979e1f522990b989737c2f9edb93597f0a80b59e25c33d13a584ef",
    "schemaversion": 6,
    "name": "bob",
    "action": "C",
    "oid": "158340da69d16150",
    "parentoid": "0000000000000001",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340d597264594",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
```

Input: Create a file in the directory

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=6cddace98d2f6eb0440f7f21adbd6623ed50b1b2fc8f6a1d17a8c890394a
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb2IiLCJ2YWx1ZXMiOnsiYWdlIjpbImRyaXZpbmciLCJkcmlua2luZyJdLCJjaXRpemVuc2hpcCI6WyJVU0EiXSwiZW1haWwiOlsicm9iLmZpZWxkaW5nQGRlY2lwaGVybm93LmNvbSJdLCJuYW1lIjpbInJvYiBmaWVsZGluZyJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSJdfX0.AAMXs9Bm1_BFGpNXJ12MvUlQTpbDbkULedKiU8nVtiTb2E-avEeMF5Yc0phV7bDCvcG8qZgCeeuGIx1AwIX4HmsPANrLm0437R7XWgALLeEoHncMuDh8vfoW3fiIG-edbnHQmUjvHcpvNsW2fHTSvb6GDzf2p9FXhLSGY-z6NA_uyNE9
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--6cddace98d2f6eb0440f7f21adbd6623ed50b1b2fc8f6a1d17a8c890394a
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "timer.go",
    "action": "C",
    "parentoid": "158340da66d4a444",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--6cddace98d2f6eb0440f7f21adbd6623ed50b1b2fc8f6a1d17a8c890394a
Content-Disposition: form-data; name="data"; filename="timer.go"
Content-Type: application/octet-stream

package client

import "time"

type Timer struct {
  Counter int64
  Start   time.Time
  Elapsed time.Duration
}

func NewTimer() *Timer {
  return &Timer{Start: time.Now()}
}

func (t *Timer) Add(e int64) {
  t.Counter += 3
}

func (t *Timer) Stop() {
  t.Elapsed = time.Since(t.Start)
}

--6cddace98d2f6eb0440f7f21adbd6623ed50b1b2fc8f6a1d17a8c890394a--
```

Output: The file was created

```bash
HTTP/1.1 200 OK
Content-Length: 1402
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:03 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 181
X-Request-Id: d07a2b10-a836-400a-846f-4caea9a776fc
X-Schema-Version: 6
X-Session-Id: session158340da6d51e520

[
  {
    "tstamp": "158340da77f085e0",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "2bf847b0df164363ee63d222a8e32eadce5b940cd9e0dfa578d0e72e00cd05d3",
    "schemaversion": 6,
    "name": "timer.go",
    "action": "C",
    "oid": "158340da77e33a5c",
    "parentoid": "158340da66d4a444",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da66e0d624",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "558/158340da6ed32558"
    },
    "size": 283,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@gmail.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "qMayQ5vYmGJIiGD+b46GhLQQjlKksA3URcS2V0jZXMZaDDmoeq1Esa66OlHWV8+CHhkKIL2EBGWxArq870Sp0E28ygftatK/Wiyp/GJ9gLU8T9U8hHJ/E0s4ERYnlVTjO4X8v79aCBRNV+6xBv7ETr1T3n8ePgHSwEc="
    },
    "encryptednonce": {
      "master": "yUY3tJc/fsGu8JQP"
    }
  }
]
```

Input: Create a file in the directory

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=30c9912e8b9e57608c17a374a4bee38d7089fc5345a47ad63b9ef380c201
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNMdWNhcyIsInZhbHVlcyI6eyJhZ2UiOlsiZHJpdmluZyIsImRyaW5raW5nIl0sImNpdGl6ZW5zaGlwIjpbIlVTQSJdLCJlbWFpbCI6WyJsdWNhcy5tb3RlbkBkZWNpcGhlcm5vdy5jb20iXSwibmFtZSI6WyJsdWNhcyBtb3RlbiJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSIsImllZWUiXX19.AO58W-CnDelUYv0ox8OgluZfrp2K34MVVQnb_WCpMhvFPJQooGDW0rAZO6jXaAVpz-7shE5dilRNPeEzNFddodqqALQH4B-x05ZJsYJwsJakTkbwubufg-_uqUQHc0Bh2hzxKfe9xcsZx3n7OkeOmoOQpuVgaJ228q_8N5tjpZ7WoS7A
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--30c9912e8b9e57608c17a374a4bee38d7089fc5345a47ad63b9ef380c201
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "timer.go",
    "action": "C",
    "parentoid": "158340da69d16150",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--30c9912e8b9e57608c17a374a4bee38d7089fc5345a47ad63b9ef380c201
Content-Disposition: form-data; name="data"; filename="timer.go"
Content-Type: application/octet-stream

package client

import "time"

type Timer struct {
  Counter int64
  Start   time.Time
  Elapsed time.Duration
}

func NewTimer() *Timer {
  return &Timer{Start: time.Now()}
}

func (t *Timer) Add(e int64) {
  t.Counter += 3
}

func (t *Timer) Stop() {
  t.Elapsed = time.Since(t.Start)
}

--30c9912e8b9e57608c17a374a4bee38d7089fc5345a47ad63b9ef380c201--
```

Output: The file was created

```bash
HTTP/1.1 200 OK
Content-Length: 1410
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:03 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 75
X-Request-Id: 92cd2818-7634-4815-93e8-fccb9b2832e5
X-Schema-Version: 6
X-Session-Id: session158340da79567a34

[
  {
    "tstamp": "158340da7db8d66c",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asBob"
    },
    "jwthash": "c752eb6f3618877d076dc1c1dc070d7f77f894c06918f2c5858714659cd6c936",
    "schemaversion": 6,
    "name": "timer.go",
    "action": "C",
    "oid": "158340da7dabda98",
    "parentoid": "158340da69d16150",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da69ff6a78",
    "objectpolicy": {
      "label": "forAnonReadBobFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "bob.smith@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9c4/158340da7af5e9c4"
    },
    "size": 283,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby bob.smith@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "dFrhN4l+h9wTODEpwm7OIsWsf+g10xvZbvjqRw686Mlte8dfU+3x0NValBrqTQfEGolvfmx345mfcvewKNHe5loJcOMtVVdGOsOdE6F5zCaBvuy6UZvbasYRA2aOl262nirMANITttErkueVAA4YEAg6mNvmzd2XzUU="
    },
    "encryptednonce": {
      "master": "U9ub6IEGpNiGvCI5"
    }
  }
]
```

Input: Do a multi-upload of one new directory, with two files into it.

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=cca271771aef2def01a939540697b8e28d7649bab739f3e7dec658bdd249
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb2IiLCJ2YWx1ZXMiOnsiYWdlIjpbImRyaXZpbmciLCJkcmlua2luZyJdLCJjaXRpemVuc2hpcCI6WyJVU0EiXSwiZW1haWwiOlsicm9iLmZpZWxkaW5nQGRlY2lwaGVybm93LmNvbSJdLCJuYW1lIjpbInJvYiBmaWVsZGluZyJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSJdfX0.AbhZX9hW0WfOsYpFUZZVf3q5igg0oc9yrHtYacypmjbv4htTfShkv7Xf8CV3VU3BP4CeL_3YCEDoFXiy8NK3bpV6ABTuf4MWI5EKaTJ3i7c8SNq6Qj8NTxemp5DXcPpIW6o6F5SIUOCNRYjzffTrTcRieNCmVk7nW3lVCkkJpHUcr8cm
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--cca271771aef2def01a939540697b8e28d7649bab739f3e7dec658bdd249
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "bigDir",
    "action": "C",
    "parentoid": "158340da66d4a444",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  },
  {
    "name": "timer.go",
    "action": "C",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/",
    "references": [
      {
        "address": -1,
        "from": "oid",
        "to": "parentoid"
      }
    ]
  },
  {
    "name": "testlocal",
    "action": "C",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/",
    "references": [
      {
        "address": -2,
        "from": "oid",
        "to": "parentoid"
      }
    ]
  }
]
--cca271771aef2def01a939540697b8e28d7649bab739f3e7dec658bdd249
Content-Disposition: form-data; name="data"; filename="timer.go"
Content-Type: application/octet-stream

package client

import "time"

type Timer struct {
  Counter int64
  Start   time.Time
  Elapsed time.Duration
}

func NewTimer() *Timer {
  return &Timer{Start: time.Now()}
}

func (t *Timer) Add(e int64) {
  t.Counter += 3
}

func (t *Timer) Stop() {
  t.Elapsed = time.Since(t.Start)
}

--cca271771aef2def01a939540697b8e28d7649bab739f3e7dec658bdd249
Content-Disposition: form-data; name="data"; filename="testlocal"
Content-Type: application/octet-stream

#!/bin/bash


export CLIENT_ADDRESS=localhost
export CLIENT_PORT=8181
export CLIENT_USE_TLS=false
export CLIENT_PREFIX=
GOCACHE=off go test -v

--cca271771aef2def01a939540697b8e28d7649bab739f3e7dec658bdd249--
```

Output: The directory was created, then file placed into directory

```bash
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:04 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 125
X-Request-Id: dff68304-3d7e-4862-8a46-f17eab5c71ab
X-Schema-Version: 6
X-Session-Id: session158340da80ad3c50

e91
[
  {
    "tstamp": "158340da87dc4228",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "7ea48eeafbb17279af2ad4d324b487f16c24d3f32d3677e576b447bc6145e3e2",
    "schemaversion": 6,
    "name": "bigDir",
    "action": "C",
    "oid": "158340da87cde534",
    "parentoid": "158340da66d4a444",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da66e0d624",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "derived": {},
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  },
  {
    "tstamp": "158340da87ed6fd0",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "7ea48eeafbb17279af2ad4d324b487f16c24d3f32d3677e576b447bc6145e3e2",
    "schemaversion": 6,
    "name": "timer.go",
    "action": "C",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da87dc4228",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "size": 283,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "7qMAqxRa8ffJqasa5d0OZNUNiIw3+GVkNB2UURits7XMTyPWY9zifshCzT5uqoSTd5jMr1WE/l5QtAhEXhcX0R1iQSeCCi60IxZPcLwmLBB35BanyIuCDoKJ/56E13rayUdMrj6mfAWjyKCShjKHyetQ/IwR2L/JwPM="
    },
    "encryptednonce": {
      "master": "McNgUdg6Ps27Eje9"
    }
  },
  {
    "tstamp": "158340da88040128",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "7ea48eeafbb17279af2ad4d324b487f16c24d3f32d3677e576b447bc6145e3e2",
    "schemaversion": 6,
    "name": "testlocal",
    "action": "C",
    "oid": "158340da87f74758",
    "parentoid": "158340da87cde534",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da87dc4228",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "97c/158340da825da97c"
    },
    "size": 143,
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "sha256plain": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "6CqR7yp8Umo4DbufGapU+My4JemywaQEYH2oeov+vFveX1bcPZx9m4G9wjyIcUT4oSDyhZ48zWa3eYBUXIRiftsdDwgKrqJkC7fgTyLxrejT91mHNnwY/Bx6AAJUnWkjFLe2khwB9JlcxL7GLtociNXGrySUa5SwXfA="
    },
    "encryptednonce": {
      "master": "AL+uvVz9T/Ca7XNJ"
    }
  }
]
0
```

Input: Update (action U) the metadata on a file that was already created. We can re-name it, move it by changing its parent, etc. Most fields are editable, except for the oid.

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=81eefa85560302f6f0fcef89b0505a53cccc3d675833aa2485b47b506fb5
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb2IiLCJ2YWx1ZXMiOnsiYWdlIjpbImRyaXZpbmciLCJkcmlua2luZyJdLCJjaXRpemVuc2hpcCI6WyJVU0EiXSwiZW1haWwiOlsicm9iLmZpZWxkaW5nQGRlY2lwaGVybm93LmNvbSJdLCJuYW1lIjpbInJvYiBmaWVsZGluZyJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSJdfX0.AbhZX9hW0WfOsYpFUZZVf3q5igg0oc9yrHtYacypmjbv4htTfShkv7Xf8CV3VU3BP4CeL_3YCEDoFXiy8NK3bpV6ABTuf4MWI5EKaTJ3i7c8SNq6Qj8NTxemp5DXcPpIW6o6F5SIUOCNRYjzffTrTcRieNCmVk7nW3lVCkkJpHUcr8cm
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--81eefa85560302f6f0fcef89b0505a53cccc3d675833aa2485b47b506fb5
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "timer-renamed.go",
    "action": "U",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--81eefa85560302f6f0fcef89b0505a53cccc3d675833aa2485b47b506fb5--
```

Output: The metadata was modified, but its stream was not re-uploaded or modified.

```bash
HTTP/1.1 200 OK
Content-Length: 1312
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:04 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 29
X-Request-Id: b3dbd31e-cd9f-4219-8e2d-dd08dc97e6a4
X-Schema-Version: 6
X-Session-Id: session158340da897e0058

[
  {
    "tstamp": "158340da8b1822e0",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "7ea48eeafbb17279af2ad4d324b487f16c24d3f32d3677e576b447bc6145e3e2",
    "schemaversion": 6,
    "name": "timer-renamed.go",
    "action": "U",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da87ed6fd0",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "text/plain",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "a1YMYefzF4YwAezqJgEyGWFvnM4EpEvbvCVlxqm9iWlDvkMWLkT2dloC4FyhkF40ObdPNp/YnUZfApjFre8EBelVriiNaXSUnQP2hpi1mCFYEtCUCOq1tGAaE1yRXSMk/vS2S/lP/nQQyTiLo3Qz/sATQu8wjk9cmKs="
    },
    "encryptednonce": {
      "master": "kCKQivQAssXMv0jT"
    }
  }
]
```

Input: Put this file into a Deleted (action D) state. You can see old versions in history, but this file is no longer in current listings.

```bash
POST /services/gm-data/0.1/write HTTP/1.1
Host: 127.0.0.1:9443
Content-Type: multipart/form-data; boundary=cd0629b511ce1b4f1c752d2c2aa4e64e8b6724f7e6f403d77e7d3026770b
Cookie: userpolicy=eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAxODI4MDMsImxhYmVsIjoiYXNSb2IiLCJ2YWx1ZXMiOnsiYWdlIjpbImRyaXZpbmciLCJkcmlua2luZyJdLCJjaXRpemVuc2hpcCI6WyJVU0EiXSwiZW1haWwiOlsicm9iLmZpZWxkaW5nQGRlY2lwaGVybm93LmNvbSJdLCJuYW1lIjpbInJvYiBmaWVsZGluZyJdLCJvcmciOlsid3d3LmRlY2lwaGVybm93LmNvbSJdfX0.AbhZX9hW0WfOsYpFUZZVf3q5igg0oc9yrHtYacypmjbv4htTfShkv7Xf8CV3VU3BP4CeL_3YCEDoFXiy8NK3bpV6ABTuf4MWI5EKaTJ3i7c8SNq6Qj8NTxemp5DXcPpIW6o6F5SIUOCNRYjzffTrTcRieNCmVk7nW3lVCkkJpHUcr8cm
X-Envoy-Upstream-Rq-Timeout-Ms: 300000

--cd0629b511ce1b4f1c752d2c2aa4e64e8b6724f7e6f403d77e7d3026770b
Content-Disposition: form-data; name="metadata"

[
  {
    "name": "byebye",
    "action": "D",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "octet/stream",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/"
  }
]
--cd0629b511ce1b4f1c752d2c2aa4e64e8b6724f7e6f403d77e7d3026770b--
```

Output: The Delete only needs a few key fields, but including the entire state will allow for the trashcan to show all relevant information about it.

```bash
HTTP/1.1 200 OK
Content-Length: 1314
Content-Type: text/plain; charset=utf-8
Date: Thu, 14 Feb 2019 14:20:04 GMT
Server: envoy
X-Envoy-Upstream-Service-Time: 27
X-Request-Id: 6852b4aa-87c9-41dd-bb9d-8affa03da7b6
X-Schema-Version: 6
X-Session-Id: session158340da8c743200

[
  {
    "tstamp": "158340da8dedf8f0",
    "tstampend": "7fffffffffffffff",
    "userpolicy": {
      "label": "asjane"
    },
    "jwthash": "7ea48eeafbb17279af2ad4d324b487f16c24d3f32d3677e576b447bc6145e3e2",
    "schemaversion": 6,
    "name": "timer-renamed.go",
    "action": "D",
    "oid": "158340da87e468e0",
    "parentoid": "158340da87cde534",
    "expiration": "7fffffffffffffff",
    "checkedtstamp": "158340da8b1822e0",
    "objectpolicy": {
      "label": "forAnonReadjaneFull",
      "requirements": {
        "f": "owner-full-ro-all",
        "a": [
          {
            "v": "email"
          },
          {
            "v": "jane.doe@deciphernow.com"
          }
        ]
      }
    },
    "mimetype": "octet/stream",
    "rname": {
      "S3": "9d0/158340da825d69d0"
    },
    "derived": {},
    "isfile": true,
    "security": {
      "label": "anonymousread ownedby jane.doe@deciphernow.com",
      "foreground": "#FFFFFF",
      "background": "#000000"
    },
    "fulldir": "/0000000000000001/",
    "encrypteddata": {
      "master": "OH0lGhfamqian77SXCa8wDcPLo7bwS9V6HJcD/rN88P4qB8uadgYxz4RfNOznCWGmsVSQp5ooS8olduEWwPWLoM/6P8jrm/W8H6ZAo+04IoFhyX5bTPy69IdNlH5OzXut6LW/iR32YVajhqFsxAZRos6n1fqpzH7iUo="
    },
    "encryptednonce": {
      "master": "ccBi4atBM3SzTV1s"
    }
  }
]
```

## Reference: Event fields

| name          | type    | format    | description                                                                                                                                                                                          |
| ------------- | ------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| schema        | integer | int32     | version of the schema from which this object came                                                                                                                                                    |
| jwt-hash      | string  | sha256hex | sha256 of JWT token we accepted                                                                                                                                                                      |
| userpolicy    | string  |           | JWT claims used when creating this event                                                                                                                                                             |
| tstamp        | string  | tstamp    | nanosecond timestamp of this objects creation time - unique per event.                                                                                                                               |
| tstampend     | string  | tstamp    | end data for this record validity, which may change                                                                                                                                                  |
| name          | string  | filename  | file/dir name without pathing                                                                                                                                                                        |
| action        | string  | CRUDXP    | actions on a file to put it into its current state                                                                                                                                                   |
| oid           | string  | tstamp    | a numeric identifier assigned to a file/dir.  happens to be a nanoseconds timestamp. (oid, tstamp) are the primary key for these events.                                                             |
| parentoid     | string  | tstamp    | oid of the parent directory.                                                                                                                                                                         |
| expiration    | string  | tstamp    | tstamp as of which this record will not come back from queries.  may legally require purges at some point.                                                                                           |
| checkedtstamp | string  | tstamp    | the tstamp of the previous version of the oid we compared against.                                                                                                                                   |
| objectpolicy  | string  |           | the rules that allow action (C R U D X P)                                                                                                                                                            |
| mimetype      | string  |           | same as content-type.  set for this object, and also for S3 blobs.                                                                                                                                   |
| rname         | string  | s3name    | ${AWS\_S3\_BUCKET}/${AWS\_S3\_PARTITION}/${rname} is where this object is in S3.  Name is assigned even if only IPFS is used.                                                                        |
| size          | integer | int64     | Same as Content-Length.                                                                                                                                                                              |
| derived       | string  |           | Fields to denote that a file is derived from another - ie: doc to PDF.  (oid,tstamp) are a primary key for the original.  type allows us to track what kind of derivation it is, such as doc-to-txt. |
| isfile        | boolean |           | Set this if it is a file.  You will get a directory if you don't set this.                                                                                                                           |
| custom        | string  |           | Put custom fields in here that gm-data does not understand, but need to be tracked by the application.                                                                                               |
| defaultfile   | string  | filename  | For a directory, if we try to stream the directory, ${defaultfile} will be the assumed name.  index.html is the default value.                                                                       |
| security      | string  |           | Security labels are written here, along with their foreground/background pen colors.                                                                                                                 |
| description   | string  |           | Description of what is in the file or directory                                                                                                                                                      |
| sha256plain   | string  |           | sha256 of the plaintext, can be used in the client to calculate a minimum number of files to send for update                                                                                         |
| blobalgorithm | string  |           | the blob algorithm.  if not specified, we store in S3 with SSECustomerKey                                                                                                                            |
| purgetstamp   | string  | tstamp    | In order to just purge a single event, supply a purgetstamp along with the oid.                                                                                                                      |
| fulldir       | string  | path      | full dir for this version of this file, beginning and terminated with a slash                                                                                                                        |
| references    | string  |           | When uploading files, an array of updates can be supplied; so that we can upload files into directories that don't yet exist.  Indices are negative relative values.                                 |
| encrypted     | string  |           | Allow for fields to be encrypted on a case-by-case basis                                                                                                                                             |

Values for action

| possible-value | description                                                                                           |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| C              | create a file, system will make up an oid.  parentoid is required.                                    |
| R              | permissions to read metadata about a file.                                                            |
| U              | update a file, with existing oid.  if oid not given, lookup by name will be done for upsert behavior. |
| D              | delete a file.  oid and objectPolicy are required.                                                    |
| X              | execute on a file/dir.  this means streaming a file, or listing dir.                                  |
| P              | purge a file, which deletes the entire history of the oid.                                            |

Values for security

| possible-value | description                                                     |
| -------------- | --------------------------------------------------------------- |
| label          | the banner string shown at top and bottom, such as SECRET       |
| background     | the background color in html color form such as #FF0000         |
| foreground     | the foreground color of the text in html format such as #FFFFFF |

Values for blobalgorithm

| possible-value | description                                                                                                                                                                                |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|                | SSECustomerKey, where we must track a crypto key, and we send the key into the S3 request to decrypt and encrypt.  This allows for the use of analytics APIs that can supply a crypto key. |
| none           | Disable encryption in the S3 bucket.  This facilitates use with APIs that cannot supply a crypto key, and don't need encryption.                                                           |
| sse            | Server Side S3 encryption.  This means that the client does not need to supply the crypto key, as S3 finds and plugs it in.                                                                |

Values for references

| possible-value | description                                      |
| -------------- | ------------------------------------------------ |
| address        | a relative value in the array of uploaded values |
| from           | the field to copy from: parentoid, oid, tstamp   |
| to             | the field to copy to: oid, tstamp                |

Values for encrypted

| possible-value | description                                                                                                     |
| -------------- | --------------------------------------------------------------------------------------------------------------- |
| name           | overwrite given name with encrypted name when this metadata is retrieved.                                       |
| security       | overwrite given security label with encrypted security when this metadata is retrieved.                         |
| blobkey        | overwrite given blobkey label with encrypted blobkey when this metadata is retrieved.                           |
| custom         | overwrite given custom attributes with encrypted custom attributes when this metadata is retrieved - per field. |
