Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/docs/Post Platform Guide/access-control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,27 @@ query {

You always get the policy actually in force. A record written with only `acl: ["*"]` reports `default_perms: 15` behind an always-passing group rather than returning the array, so you can render one consistent view without caring how the record was written.

## Naming a group instead of a person

A grant or denial can name a group eName, and it resolves to the group's members when the decision is made — so membership changes take effect without rewriting any policy.

```json
{ "v": 1,
"grants": [ { "ename": "@owner", "perms": 15 },
{ "ename": "@9f0e1d2c-3b4a-5968-7766-554433221100", "perms": 1 } ],
"denials": { "enames": [], "conditions": [] },
"default_perms": 0,
"require": [] }
```

You do not have to normalise your group records first. Participants are read from `members`, `memberIds`, `participants`, `participantIds`, `admins` and `owner`, and each entry may be **either an eName or the id of that member's profile record** — the two shapes platforms actually write. A profile id resolves through the record's own `ename` field, falling back to the vault it lives in.

Worth knowing:

- **Admins and the owner count as members.** Every participant field is unioned, so a group grant reaches them too. If you need admins treated differently, name them directly rather than relying on the group.
- **A group grant is the least specific kind.** A direct grant to the user or the platform overrides it entirely and is not combined with it.
- **A group whose record this eVault does not hold cannot be resolved.** A grant naming it hands out nothing; a denial naming it stays in force. Uncertainty never widens access, but it can refuse someone you expected to admit.

## Things that will bite you

**A grant is final.** If your platform is named in `grants`, that grant decides the answer on its own. It never falls through to `default_perms` — so a platform granted `1` on a record whose `default_perms` is `15` has read access, not full access. Being named is not always an upgrade.
Expand DownExpand Up@@ -197,7 +218,6 @@ List queries behave differently again: a record you may not read is **omitted fr

Two parts of the protocol document are specified but not connected, and a policy relying on them will not behave as written:

- **Groups.** Group membership is not resolved, so a grant or denial naming a group matches nobody. A group grant simply fails to apply; a **group denial silently fails to deny**, which is the dangerous direction. Name parties individually for now.
- **Ontology conditions.** No evaluator is wired in, so any condition fails. A `require` group containing conditions can never pass, and a deny condition always fires and refuses everyone. Until that lands, use only `grants`, `denials.enames`, and `require: []` or `require: [[]]`.

Enforcement is eVault-side. Platforms and the adapter do not evaluate policies themselves, so do not treat a policy as a reason to skip your own authorization checks.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/W3DS Basics/glossary.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ An entry in an [ACL](#access-control-list-acl) pairing a party's [eName](#web-30

## Group

An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks.
An [Entity](#entity): a reference to a number of users within the MetaState that holds its own [W3ID](#web-30-identifier-w3id-ename) and [eVault](#evault). It is often seen as a "group" in social networks. In an [ACL](#access-control-list-acl) a group eName is resolved to its members' eNames when a decision is made, so naming a group stays correct as its membership changes.

---

Expand Down
32 changes: 31 additions & 1 deletion docs/docs/W3DS Protocol/Access-Control.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,36 @@ Grants tied at the same specificity — duplicates, or two groups the party belo

A direct grant is final. A named party never falls through to the ontology half, whether its grant allowed the action or not.

### How a group resolves

A group eName is not a party in its own right — it stands for the people in it, and is resolved to their eNames when the decision is made. Naming a group in a policy therefore stays correct as the group's membership changes, with nothing to rewrite.

The group's record is found either in the group's own vault or by its `ename` field naming the group, and its participants are read from whichever fields it carries — `members`, `memberIds`, `participants`, `participantIds`, `admins`, `owner`. A group's members are the union of all of them, so an admin is a member.

A participant may be named two ways, and both are accepted:

| Written as | Example | Resolved by |
|---|---|---|
| An eName | `@7b9c2e1a-…` | Taken as-is. |
| A profile record's id | `4f1a8c30-…` | Following the record to the eName behind it. |

Both occur in practice — `GroupManifest.members` holds eNames while `Group.participantIds` holds profile ids — so a policy naming a group works regardless of which shape the group was written with.

When a participant is given as a profile id, the eName is taken from the record's own `ename` field where it has one, and otherwise from the vault the record lives in. The record's own statement wins because the same profile syncs into several vaults, so the vault it happens to sit in does not reliably identify its subject.

An id that resolves to nothing is skipped rather than treated as a member.

### When membership cannot be determined

A lookup that fails is not the same as a party being shown not to be a member, and the two lead to opposite answers:

- A **grant** to a group applies only on proof of membership. Uncertainty withholds it.
- A **denial** naming a group applies unless the party is shown *not* to be a member. Uncertainty holds the denial.

So a group whose record cannot be read is safe in both directions: it hands out nothing and it stops removing nothing.

Where no group resolver is configured at all, groups simply do not resolve — that is the feature switched off rather than a failed lookup, and group grants and denials alike match nobody.

## Denials

A denial removes access regardless of any grant. **Deny always wins**, with no exceptions — it is the one place where specificity does not decide the outcome.
Expand DownExpand Up@@ -240,9 +270,9 @@ Reserved permission bits exist for the same reason: they are refused today so th

## Current limits

- **Group membership is not resolved yet.** A grant or denial naming a group matches nothing. For grants that is fail-closed; for denials it is fail-**open**, so group denials are not usable yet.
- **Condition evaluation is a seam, not yet connected.** eVault accepts an evaluator but none is wired in, so conditions currently fail closed: a `require` group containing conditions cannot pass, and a deny condition always fires. Until an evaluator is connected, write policies that use `grants`, `denials.enames`, and empty-group `require` only.
- **Enforcement is eVault-side.** The Web3 Adapter and platforms do not evaluate `_acl` yet.
- **Group resolution reads records this eVault holds.** A group whose record has not synced here cannot be resolved, and is treated as undeterminable — see above for what that means in each direction.
- `default_perms` above READ for unnamed parties is unsettled under the current sync model.

## See also
Expand Down
Loading
Loading