Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: sketch of the power-line concept #3

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
86 changes: 86 additions & 0 deletions notes/conds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# delegation clause

In 0.x verison of UCANs has an open-ended `nb` field that most users use to constraints delegated capabilities. Open ended nature makes interop very difficult as how to interpert those constraints are not covered by spec. In 1.0 we would like to define standard but extensible constarints language so that implementations can interop unless user space extensions are utilized. By allowing user space extensions of the constraint language we also allow user space to innovate at the cost of interop.
Gozala marked this conversation as resolved.
Show resolved Hide resolved

<!--
## s-expression based language

### Operators

| Operator | Function Signature | Description | Example |
| ------------ | --------------------------------- | ------------------------------------------ | ---------------------------------------------- |
| `/${string}` |`[] -> Any` | Selects value using JSON Pointer | `["/foo/0"]` |
| |`[] -> Any` | Selects whole value like "" in JSON Pointer | `[]` |
| `==` |`[Exp<Any>, Exp<Any>] -> Bool` | Equality | `["==", ["/size"], 1]` |
| `<` |`[Exp<Num>, Exp<Num>] -> Bool` | Less than | `["<", ["/size"], 1024]` |
| `<=` |`[Exp<Num>, Exp<Num>] -> Bool` | Less than or equal | `["<=", ["/size"], 1024]` |
| `>` |`[Exp<Num>, Exp<Num>] -> Bool` | Greater than | `[">", ["@", "/size"], 0]` |
| `>=` |`[Exp<Num>, Exp<Num>] -> Bool` | Greater than or equal | `[">=", ["@", "/size"], 32]` |
| `not` |`[Expr<Bool>] -> Bool` | Negation | `["not", ["<=", ["/size"], 128]` |
| `match` |`[Expr<Str>, Exp<Str>] -> Bool` | String wildcard | `["match", ["/to"], "*@foo.com"]` |
| `or` |`Exp<Bool>[] -> Bool` | Logical **or** combinator | `["or", [">", ["/n"], 0], ["==", ["/n"], 0]]` |
| `and` |`Exp<Bool>[] -> Bool` | Logical **and** combinator | `["and", [">", ["/n"], 0], ["<", ["/n"], 10]]`|

## Examples

Delegates `email/send` command on `did:key:alice` with a following constraint: Email is not send to address on `gmail.com`, or someone from `fission.codes` should be cc-ed.

```js
{
"sub": "did:key:alice",
"iss": "did:key:alice",
"aud": "did:key:bob",
"cmd": "email/send",
"cond": [
["or",
// no recipient should be at `@gmail.com`
["every", ["/to"], ["not", ["match", [], "*@gmail.com"]]],
// or cc should include @fission.codes
["some", ["/cc"], ["match", [], "*@fission.codes"]]
]
]
}
```
-->

## logic programming language

We are also considering logic programming alternative inspired by datomic query styntax.

### concrete proposal for the inital release

Here's a sketch that shows the features

```js
{
"iss": "did:key:alice",
// ...
"policy": [
["some", "?x"],
["$args", "foo..bar.[].baz", "?x"],
["match", "?x", "*@example.com"],

["$args", "foo.quux", "?y"],
["?y", ">", 42],
["?x", "<", "?y"]
expede marked this conversation as resolved.
Show resolved Hide resolved
]
Gozala marked this conversation as resolved.
Show resolved Hide resolved
}
Gozala marked this conversation as resolved.
Show resolved Hide resolved
```

Gozala marked this conversation as resolved.
Show resolved Hide resolved
Of note:

* Top-level array checks that every logic variable returns values (which means that it matches the content of the args array)
* Selector syntax based on JSONPath / JSON Poiniter / jQ
* `"foo..bar.[].baz"` ~ `foo.{*recursive descent*}.bar.{ALL}.baz`
* Variables look like `"?x"` for user defined, and `"$y"` for system/kernel
* If you have a string that matches these, you have to escape them
* these give the ability to run predicate logic in the middle of a sector
* `[[<node>, <path>, "?x"], [<pred>, "?x", <expr>]`
* Declarative matching from `args` onwards
* Anything of the form `[<node>, <selector>, <value or variable>]`
* Can avoid namespace conflicts because users define variable names locally
* Recurses if needed
* A trivial case: `[["args", "some.path", "?x"], ["?x", "more.path", 42]]`
* Matches `args.some.path.more.path == 42`
* Use of `args` to start the path allows us to open up the synatx over time if we want
* Plus it's very declarative
144 changes: 144 additions & 0 deletions notes/power-line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Delegation

## Root Cert

```js
[
{"/": {"bytes": {"0xSignature"}}, // You must check this, and they're really small (except RSA)
[
{"/": {"bytes": {"0xVarsigHeader"}}, // Array to force this to the front
{
"ucan/1.0/d": { // "This is a v1.0 UCAN delegation"
"sub": "did:key:alice",
"iss": "did:key:alice",
"aud": "did:key:bob",
"cmd": "/http",
"cond": [
{"match": {"url": "https://example.com/api/v1/*"}},
{
"or": [
{"==": {"headers": {"accept": "application/json"}}}}, // `==` is a kind of match 🤔
{"==": {"headers": {"content-type": "application/json"}}
]
}
],
"exp": 1234567890,
"meta": {
"tags": ["development", "test-suite", "protocol-labs"]
}
}
}
]
]
```

## Inner Chain — Normal

```js
[
{"/": {"bytes": {"0xSignature"}},
[
{"/": {"bytes": {"0xVarsigHeader"}},
{
"ucan/1.0/d": {
"sub": "did:key:alice",
"iss": "did:key:bob",
"aud": "did:key:carol",
"cmd": "/http/get", // Attenuated
"cond": [
{"match": {"url": "https://example.com/api/v1/users/*"}} // Attenuated
// "SHOULD" include the headers here too as a best practice, but no longer required in the spec
],
"exp": 1234567890,
"meta": {
"tags": ["development", "test-suite", "protocol-labs"]
}
}
}
]
]
```

## Inner Chain — Powerline

Because it confuses some people with traditional capabilties backgrounds, I'm going to _try_ calling our modified powerbox-like pattern a "powerline" because it works on delegation chains.

```js
[
{"/": {"bytes": {"0xSignature"}},
[
{"/": {"bytes": {"0xVarsigHeader"}},
{
"ucan/1.0/d": {
// No sub field
"iss": "did:key:carol",
"aud": "did:key:dora",
"cmd": "/http", // Any HTTP command
"cond": [], // No conditions for this specific example
"exp": 1234567890,
"meta": {
"tags": ["development", "test-suite", "protocol-labs"]
}
}
}
]
]
```

Just another example not in the invocation chain example to show another case ⬇️

```js
[
{"/": {"bytes": {"0xSignature"}},
[
{"/": {"bytes": {"0xVarsigHeader"}},
{
"ucan/1.0/d": {
// No aud field
"iss": "did:key:paul",
"aud": "did:key:sara",
"cmd": "/dns", // Any DNS command
"cond": [
{"==": {"rr-type": "TXT"}} // Only TXT records
],
"exp": 1234567890,
"meta": {
"tags": ["development", "test-suite", "protocol-labs"]
}
}
}
]
]
```

# Invocation

```js
[
{"/": {"bytes": {"0xSignature"}},
[
{"/": {"bytes": {"0xVarsigHeader"}},
{
"ucan/1.0/i": { // "This is a v1.0 UCAN invocation"
"sub": "did:key:alice",
"iss": "did:key:dora",
"aud": "did:key:frank", // Optional field. If omitted, assumed to be the same as `aud`.
"cmd": "/http/get",
"args": {
"url": "https://example.com/api/v1/users/42",
"accept": "application/json"
},
"prf": [
{"/": "bafy...carol-dora-powerline"},
{"/": "bafy...bob-carol"},
{"/": "bafy...alice-bob"}
],
"exp": 1234567890,
"meta": {
"tags": ["development", "test-suite", "protocol-labs"]
}
}
}
]
]
```