68.4 KB
Newer Older
1 2 3
#+TITLE: OcapPub: Towards networks of consent
#+AUTHOR: Christopher Lemmer Webber

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
4 5
/This paper released under the Apache License version 2.0; see [[file:./LICENSE.txt][LICENSE.txt]] for details./

/For a broader overview of various anti-spam techniques, see [[][AP Unwanted Messages]], which is in many ways informed this document but currently differs in some implementation rollout differs.  (These two documents may converge.)/

8 9
* Conceptual overview

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
The federated social web is living in its second golden age, after
the original success of StatusNet and OStatus in the late 2000s.
A lot of this success has been around unification of adoption of a
single protocol, [[][ActivityPub]], to connect together the many different
instances and applications into a unified network.

Unfortunately from a security and social threat perspective, the way
ActivityPub is currently rolled out is under-prepared to protect its
In this paper we introduce OcapPub, which is compatible with the original
ActivityPub specification.
With only mild to mildly-moderate adjustments to the existing network,
we can deliver what we call "networks of consent": explicit and
intentional connections between different users and entities on the
The idea of "networks of consent" is then implemented on top of a
security paradigm called "object capabilities", which as we will see
can be neatly mapped on top of the actor model, on which ActivityPub
is based.
29 30 31
While we do not claim that all considerations of consent can be modeled
in this or any protocol, we believe that the maximum of consent that is
/possible/ to encode in such a system can be encoded.
32 33 34 35 36 37

Paradoxically, what may initially appear to be a restriction actually
opens up the possibility of richer interactions than were previously
possible on the federated social web while better preserving the
intentions of users on the network.

38 39
** ActivityPub

40 41 42
# - ActivityPub is an actor model protocol.
# - The general design can be understood from the overview section of the spec

43 44 45 46 47 48 49 50 51
[[][ActivityPub]] is a federated social network protocol.
It is generally fairly easily understood by reading the
[[][Overview section of the standard]].
In short, just as anyone can host their own email server, anyone can
host their own ActivityPub server, and yet different users on different
servers can interact.
At the time of writing, ActivityPub is seeing major uptake, with
several thousand nodes and several million registered users (with the
caveat that registered users is not the same as active users).
52 53 54 55
The wider network of ActivityPub-using programs is often called
"the fediverse" (though this term predates ActivityPub, and was
also used to describe adoption of its predecessor, OStatus).

56 57 58 59
ActivityPub defines both a client-to-server and server-to-server
protocol, but at this time the server-to-server protocol is what is
most popular and is the primary concern of this article.

60 61 62
# - In general, most of the design of ActivityPub is fairly clean, with
#   a few exceptions

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
ActivityPub's core design is fairly clean, following the
[[][actor model]].
Different entities on the network can create other actors/objects
(such as someone writing a note) and communicate via message passing.
A core set of behaviors are defined in the spec for common message
types, but the system is extensible so that implementations may define
new terms with minimal ambiguity.
If two instances both understand the same terms, they may be able to
operate using behaviors not defined in the original protocol.
This is called an "open world assumption" and is necessary for a
protocol as general as ActivityPub; it would be extremely egotistical
of the ActivityPub authors to assume that we could predict all future
needs of users.[fn:json-ld]

#   - (json-ld conversations outside of the scope of this particular post)

Unfortunately (mostly due to time constraints and lack of consensus),
even though most of what is defined in ActivityPub is fairly
clean/simple, ActivityPub needed to be released with "holes in the
Certain key aspects critical to a functioning ActivityPub server are
not specified:

86 87 88
#   - authentication is not specified.  The community has settled
#     on using http signatures for signing requests, though there is no
#     "clean agreement" on how to attach signatures *to* posts yet.
#   - authorization is not specified

91 92 93 94 95 96
 - Identity verification is not specified.
   ("Identity verification" is the same as "authentication", but since
   "authentication" sounds confusingly too similar to "authorization",
   we are not generally using that term in this document.)
   Identify verification is important to verify "did this entity
   really say this thing".[fn:did-you-say-it]
97 98 99 100 101 102 103 104 105
   However, the community has mostly converged on using [[][HTTP Signatures]]
   to sign requests when delivering posts to other users.
   The advantage of HTTP Signatures is that they are extremely simple
   to implement and require no normalization of message structure;
   simply sign the body (and some headers) as-you-are-sending-it.
   The disadvantage of HTTP Signatures is that this signature does
   not "stick" to the original post and so cannot be "carried around"
   the network.
   A minority of implementations have implemented some early versions
   of [[][Linked Data Proofs]] (formerly known as "Linked Data Signatures"),
107 108 109 110
   however this requires access to a normalization algorithm that not
   all users have a library for in their language, so Linked Data Proofs
   have not as of yet caught on as popularly as HTTP Signatures.

111 112 113 114 115 116
 - Authorization is also not specified.
   As of right now, authorization tends to be extremely ad-hoc in
   ActivityPub systems, sometimes as ad-hoc as unspecified heuristics
   from tracking who received messages previously, who sent a message
   the first time, and so on.
   The primary way this is worked around is sadly that
117 118 119 120
   interactions which require richer authorization simply have not
   been rolled out onto the ActivityPub network.

Compounding this situation is the general confusion/belief that
121 122 123
authorization must stem from identity verification (again, partly
because "authentication" is often used for "identity verification",
and that term /sounds/ in English too similar to "authorization").
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
This document aims to show that not only is this not true, it is also
a dangerous assumption with unintended consequences.
An alternative approach based on "object capabilities" is
demonstrated, showing that the actor model itself, if we take it at
its purest form, is itself already a sufficient authorization system.

#   - sharedInbox is a break from the actor model protocol and was a late
#     addition

Unfortunately there is a complication.
At the last minute of ActivityPub's standardization, =sharedInbox= was
added as a form of mutated behavior from the previously described
=publicInbox= (which was a place for servers to share public content).
The motivation of =sharedInbox= is admirable: while ActivityPub is based
on explicit message sending to actors' =inbox= endpoints, if an actor
on server A needs to send a message to 1000 followers on server B,
why should server A make 1000 separate requests when it could do it
in one?
A good point, but the primary mistake in how this one request is made;
rather than sending one message with a listing of all 1000 recipients
on that server (which would preserve the actor model integrity),
it was advocated that servers are already tracking follower information,
so the receiving server can decide whom to send the message to.
Unfortunately this decision breaks the actor model and also our suggested
solution to authorization; see [[][MultiBox]] for a suggestion on how we
can solve this.
150 151 152
This is more serious than it seems; we cannot proceed to make the system
much safer to use without throwing out =sharedInbox= since we will
lose our ability to make intentional, directed messages.
153 154 155 156 157 158 159 160 161 162 163 164

# - What to do about the holes in the spec?  Many community members have
#   asked that we codify current behavior.  However, as this document lays
#   out, some of the ways those holes were filled may be causing problems
#   and we may want to consider how to best redirect them without throwing
#   out the network as it has been deployed.

# - Nonetheless, ActivityPub has achieved major adoption.  ActivityPub
#   has the good fortune that its earliest adopters were frequently
#   people who are actively concerned with human rights and the
#   well-being of marginalized groups.

165 166 167 168
Despite these issues, ActivityPub has achieved major adoption.
ActivityPub has the good fortune that its earliest adopters tended to
be people who cared about human rights and the needs of marginalized
groups, and spam has been relatively minimal.

[fn:json-ld] The technology that ActivityPub uses to accomplish this is
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
  called [[][json-ld]] and admittedly has been one of the most controversial
172 173 174 175 176 177 178 179 180
  decisions in the ActivityPub specification.
  Most of the objections have surrounded the unavailability of json-ld
  libraries in some languages or the difficulty of mapping an open-world
  assumption onto strongly typed systems without an "other data" bucket.
  Since a project like ActivityPub must allow for the possibility of
  extensions, we cannot escape open-world assumptions.
  However, there may be things that can be done to improve happiness
  about what extension mechanism is used; these discussions are out of
  scope for this particular document, however.

182 183 184
[fn:did-you-say-it] Or more accurately, since users may appoint
  someone else to manage posting for them, "was this post really made
  by someone who is authorized to speak on behalf of this entity".

** The mess we're in

188 189
# - "there are no nazis on the fediverse"

190 191 192 193 194 195 196 197 198 199 200
  [[][Mastodon Is Like Twitter Without Nazis, So Why Are We Not Using It?]]
   -- Article by Sarah Jeong, which drove much interest in
      adoption of Mastodon and the surrounding "fediverse"

At the time this article was written about Mastodon (by far the most
popular implementation of ActivityPub, and also largely responsible
for driving interest in the protocol amongst other projects), its
premise was semi-true; while it wasn't that there were no neo-nazis on
the fediverse, the primary group which had driven recent adoption were
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
themselves marginalized groups who felt betrayed by the larger
202 203 204 205 206 207
centralized social networks.
They decided it was time for them to make homes for themselves.
The article participated in an ongoing narrative that (from the
author's perspective) helped reinforce these community norms for the

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
208 209 210 211 212
However, there is nothing about Mastodon or the fediverse at large
(including the core of ActivityPub) which /specifically/ prevents
nazis or other entities conveying undesirable messages (including
spam) from entering the network; they just weren't there or were in
small enough numbers that instance administrators could block them.
213 214 215
[[][the fediverse no longer has the luxury of claiming to be neo-nazi free]]
(if it ever could).
216 217 218 219 220 221
The risk that people from marginalized groups, which the fediverse has
in recent history appealed to, are now at risk from targeted harassment
from these groups.
Even untargeted messages, such as general hate speech, may have a
severe negative impact on one's well being.
Spam, likewise, is an increasing topic of administrators and
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
implementers (as it has historically been for other federated social
223 224 225 226 227 228 229 230 231 232 233 234 235
protocols, such as email/SMTP and OStatus during its heyday).
It appears that the same nature of decentralized social networks in
allowing marginalized communities to make communities for themselves
also means that harassment, hate speech, and spam are not possible
to wholly eject from the system.

Must all good things come to an end?

** Unwanted messages, from spam to harassment

One thing that spam and harassment have in common is that they are the
delivery of messages that are not desired by their recipient.
However, it would be a mistake to claim that the impact of the two are
236 237
the same: spam is an annoyance, and mostly wastes time;
harassment wastes time, but may also cause trauma.
238 239 240 241 242 243 244 245

Nonetheless, despite the impact of spam and harassment being very
different, the solutions are likely very similar.
Unwanted messages tend to come from unwanted social connections.
If the problem is users receiving unwanted messages, perhaps the
solution comes in making intentional social connections.
But how can we get from here to there?

246 247
** Freedom of speech also means freedom to filter

248 249 250 251 252 253 254
As an intermediate step, we should throw out a source of confusion:
what is "freedom of speech"?
Does it mean that we have to listen to hate speech?

We can start by saying that freedom of speech and the freedom of
assembly are critical tools.
Indeed, these are some of the few tools we have against totalitarian
authorities, which the world is increasingly threatened by.
256 257 258 259 260 261 262 263 264 265 266 267

Nonetheless, we are under severe threat from [[][neo-fascists]].
Neo-fascists play an interesting trick: they exercise their freedom of
speech by espousing hate speech and, when people say they don't want
to listen to them, say that this is censorship.

Except that freedom of speech merely means that you have the freedom
to /exercise/ your speech, somewhere.
It does not mean that everyone has to listen to you.
You also have the right to call someone an asshole, or stop listening
to them.
There is no requirement to read every spam that crosses your email
Alex Schroeder's avatar
Alex Schroeder committed
inbox to preserve freedom of speech; neither is there a requirement to listen to
269 270 271 272
someone who is being an asshole.
The freedom to filter is the complement to freedom of speech.
This applies to both individuals and to communities.

273 274
Indeed, the trick of neo-fascists ends in a particularly dangerous
hook: they are not really interested in freedom of speech at all.
275 276 277 278 279 280 281 282 283 284 285 286 287 288
They are interested in freedom of /their/ speech, up until the point
where they can gain enough power to prevent others from saying things
they don't like.
This is easily demonstrated; see how many people on the internet are
willing to threaten women and minorities who exercise the smallest
amount of autonomy, yet the moment that someone calls them out on
their /own/ bullshit, they cry censorship.
Don't confuse an argument for "freeze peach" for an argument for
"free speech".

Still, what can we do?
Perhaps we cannot prevent assholes from joining the wider social
network... but maybe we can develop a system where we don't have to
hear them.

290 291 292 293 294 295
** Did we borrow the wrong assumptions?

  "What if we're making the wrong assumptions about our social networks?
  What if we're focusing on breadth, when we really should be focusing
  on depth?"
296 297 298
    -- approximate quote from a conversation with Evan Prodromou,
       initial designer of both ActivityPub and OStatus' protocol
299 300

301 302 303
What is Evan trying to say here?
Most contemporary social networks are run by surveillance capitalist
organizations; in other words, their business model is based on as much
Alex Schroeder's avatar
Alex Schroeder committed
"attention" as possible since they can sell it to advertisers.
305 306 307 308
Whether or not capitalism is a problem is left as an exercise for the
reader, but hopefully most readers will agree that a business model
based on destroying privacy can lead to undesirable outcomes.
One such undesirable outcome is that these companies subtly affect the
Alex Schroeder's avatar
Alex Schroeder committed
way people interact with each other: not dependent on what is
310 311 312 313 314
healthiest for people and their social relationships, but based on what
will generate the most advertising revenue.

One egregious example of this is the prominence of the "follower
count" in contemporary social networks, particularly Twitter.
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
315 316
When visiting another user's profile, even someone who is aware of and
dislikes its effect will have trouble not comparing follower counts
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
and mentally using this as a value judgement, either about the other
person or about themselves.
Users are subconsciously tricked into playing a popularity contest,
whether they want to play that game or not.
Rather than being encouraged to develop a network of meaningful
relationships with which they have meaningful communications, users
face a subconscious pressure to tailor their messaging and even who
else they follow to maximize their follower count.

So why on earth would we see follower counts also appear prominently
on the federated social web, if these tools are generally built by
teams that do not benefit from the same advertising structure?
The answer is simple: it is what developers and users are both
familiar with.
This is not an accusation; in fact, it is a highly sympathetic
position to take: the cost, for developers and users alike, of
developing a system is lower by going with the familiar rather than
researching the ideal.
But the consequences may nonetheless be severe.

So it is too with how we build our notion of security and
authorization, which developers tend to mimic from the systems they
have already seen.
Why wouldn't they?
But it may be that these patterns are, in fact, anti-patterns.
it may be time for some re-evaluation.

344 345
# - social networks: breadth vs depth?
#   - wholesale borrowing of surveillance capitalist assumptions

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
** We must not claim we can prevent what we can not

349 350 351 352 353 354 355 356
  "By leading users and programmers to make decisions under a false
  sense of security about what others can be prevented from doing,
  ACLs seduce them into actions that compromise their own security."
   -- From an analysis from Mark S. Miller on
      [[][whether preventing delegation is even possible]]

357 358 359
# - introduce ocap community phrase
# - introduce revised version

360 361
The object capability community has a phrase that is almost, but not
entirely, right in my book: "Only prohibit what you can prevent".
Alex Schroeder's avatar
Alex Schroeder committed
362 363
This seems almost right, except that there may be things in-bound
of a system that we cannot technically prevent, yet we prohibit from
364 365
occurring anyhow, and which we may enforce at another abstraction
layer, including social layers.
So here is a slightly modified version of that phrase: "We must not
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
claim we can prevent what we can not."
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

This is important.
There may be things which we strongly wish to prevent on a protocol
level, but which are literally impossible to do on only that layer.
If we misrepresent what we can and cannot prevent, we open our users
to harm when those things that we actually knew we could not prevent
come to pass.

A common example of something that cannot be prevented is the copying
of information.
Due to basic mathematical properties of the universe, it is literally
impossible to prevent someone from copying information once they have
it on the data transmission layer alone.
This does not mean that there aren't other layers where we can't
prohibit such activity, but we shouldn't pretend we can prevent it
at the protocol layer.

For example, Alice may converse with her therapist over the
protocol of sound wave vibrations (ie, simple human speech).
Alice may be expressing information that is meant to be private,
388 389 390
but there is nothing about speech traveling through the air that
prevents the therapist from breaking confidence and gossiping
about it to outside sources.
But Alice could take her therapist to court, and her therapist could
392 393 394
lose her license.
But this is not on the protocol layer of ordinary human speech itself.
Similarly, we could add a "please keep this private" flag to
ActivityPub messages so that Alice could tell Bob to please not
share her secrets.
Bob, being a good friend, will probably comply, and maybe his client
398 399
will help him cooperate by default.
But "please" or "request" is really key to our interface, since from
400 401 402
a protocol perspective, there is no guarantee that Bob will comply.
However this does not mean there are no consequences for Bob if he
betrays Alice's trust: Alice may stop being his friend, or at least
403 404
unfollow him.

405 406 407
Likewise, it is not possible to attach a protocol-enforceable "do not
delegate" flag onto any form of authority, whether it be an ocap or an
408 409
If Alice tells Bob that Bob, and Bob alone, has been granted access to
this tool, we should realize that as long as Bob wants to cooperate
with Mallet and has communication access to him, he can always set up
411 412
a proxy that can forward requests to Alice's tool as if they were

We are not endorsing this, but we are acknowledging it.
415 416 417 418
Still, there is something we can do: we could wrap Bob's access to
Alice's tool in such a way that it logs that this is the capability
Alice handed to Bob being invoked every time it is invoked, and
disable access if it is misused... whether due to Bob's actions,
or Mallet's.
420 421
In this way, even though Alice cannot prevent Bob from delegating
authority, Alice can hold Bob accountable for the authority granted
422 423 424 425 426 427
to him.

If we do not take this approach, we expose our users to harm.
Users may believe their privacy is intact and may be unprepared for
the point at which it is violated, and so on and so on.

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
We must not claim we can prevent what we can not.
This will be a guiding principle for the rest of this document.

431 432
** Anti-solutions

433 434 435 436 437
In this section we discuss "solutions" that are, at least on their
own, an insufficient foundation to solve the pressing problems this
paper is trying to resolve.
Some of these might be useful complementary tools, but are
structurally insufficient to be the /foundation/ of our approach.
438 439 440

*** Blocklists, allow-lists, and perimeter security

441 442 443 444 445 446 447 448 449 450 451
  "With tools like access control lists and firewalls, we engage in
  'perimeter defense', which is more correctly described as 'eggshell
  defense'. It is like an eggshell for the following reason: while an
  eggshell may seem pretty tough when you tap on it, if you can get a
  single pinhole anywhere in the surface, you can suck out the entire
  yoke. No wonder cybercrackers laugh at our silly efforts to defend
  ourselves. We have thrown away most of our chances to defend
  ourselves before the battle even begins."
    -- Marc Stiegler, [[][E in a Walnut]]

453 454 455 456 457 458
Blocklists and allow-lists appear, at first glance, to be a good
foundation for establishing trust or distrust on a social network.
Unfortunately, both solutions as a foundation actually shake the
structure of the system apart after long enough.

This isn't to say we aren't sympathetic to the goals of block-lists
and allow-lists, but that they don't work long term.
460 461 462 463 464
In order to understand this, we need to look at the problem from
several sides.

**** The Nation-State'ification of the Fediverse

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
Part of the major narrative of the federated social network at the
moment is that running an instance is an excellent opportunity to host
and support a community, maybe of people like you or people you like.
Different rules may apply differently on different instances, but
that's okay; choose an instance that matches your personal philosophy.

So you run an instance.
On your instance, maybe some bad behavior happens from some users.
You begin to set up policies.
You perhaps even ban a user or two.
But what about bad behavior that comes from the outside?
This is a federated social network, after all.

Blocking a user is fine.
Blocking an instance or two is fine.
But what happens when anyone can spawn a user at any time?
What happens when anyone can spawn an instance at any time?
Self-hosting, which originally seemed like something to aspire
to, becomes a threat to administrators; if anyone can easily spawn
an instance, host administrators and users are left playing
whack-a-mole against malicious accounts.
It seems like our model is not set up to be able to handle this.

Soon enough, you are tired of spending all your free time
administrating the instance blocklist.
You begin to set up the ability to share automatic blocklists between
But the governance of these lists seems fraught at best, and prone
to in-fighting.
Worse yet, you seem to have improperly gotten on several blocklists
and you're not sure how.
The criteria for what is and isn't acceptable behavior between
instances varies widely, and it's unclear to what extent it's worth

It dawns on you: the easier approach isn't a deny-list, it's an
allow-list (aka a whitelist).
Why not just trust these five nodes?
It's all you have energy for anymore.

Except... what if you aren't one of the five major nodes?
Suddenly you see that other nodes are doing the same thing, and
people are de-federating from /you/.
It's not worth running a node anymore; if you aren't on one of the top
five... hold up... top three instances anymore, nobody gets your
messages anyway.

This is the "nation-state'ification of the fediverse", and it results
in all the xenophobia of nation-states traditionally.
Sure, border guards as a model on the fediverse aren't as bad as
in the physical world; they can't beat you up, they can't take your
things (well, maybe your messages), they can't imprison you.
And yet the problem seems similar.
And it's only going to get worse until we're back at centralization

A fundamental flaw occurred in our design; we over-valued the role
that instances should play /altogether/.
While there is nothing wrong with blocking an instance or two, the
network effect of having this be the foundation is re-centralization.

526 527
**** Where do communities really live?

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
528 529
Furthermore, it doesn't even reflect human behavior; few people
belong to only one community.
Alice may be a mathematics professor at work, a fanfiction author
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
in her personal time, and a tabletop game enthusiast with her friends.
The behaviors that Alice exhibits and norms of what is considered
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
acceptable may shift radically in each of these communities, even if
in all of these communities she is Alice.
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
535 536 537
This isn't duplicitous behavior, this is normal human behavior,
and if our systems don't allow for it, they aren't systems that
serve our users' needs.
But consider also that Alice may have one email account, and yet may
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
539 540
use it for all three of these different communities' email mailing
541 542
Those mailing lists may be all on different servers, and yet Alice
is able to be the right version of Alice for each of those communities
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
543 544 545 546 547 548
as she interacts with them.
This seems to point at a mistake in assumptions about the federated
social web: the instance is not the community level, because users
may have many varying communities on different instances, and each
of those instances may govern themselves very differently.

549 550
**** Not only a social problem, but a security problem too

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
So far the problems with "perimeter security" described above have
been examples restricted to the social level.
As it turns out, perimeter security has another problem when we start
thinking about authorization called the "confused deputy problem".
For example, you might run a local process and consider that it
is localhost-only.
Whew!  Now only local processes can use that program.
Except now we can see how "perimeter security" is "eggshell security"
by how easy it is to trick another local program to access resources
on our behalf.
An excellent example of this where
[[][Guile's live-hackable REPL suffered a remote execution vulnerability]].
Except... Guile didn't appear to "do anything wrong", it restricted
its access to localhost, and localhost-only.
But a browser could be tricked into sending a request with code that
executed commands against the localhost process.
Who is to blame?
Both the browser and the Guile process appeared to be following
their program specifications, and taken individually, neither seemed
And yet combined these two programs could open users to serious

Perimeter security is eggshell security.
And the most common perimeter check of all is an identity check,
the same paradigm used by Access Control Lists.
It turns out these problems are related.

579 580
*** Access Control Lists

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
Up until recently, if you drove a car, the car did not determine
whether you could drive it based on who you are, as your identity.
If you had a key, you could drive it, and it didn't matter who you

Nonetheless, since Unix based its idea of authority on "who you are",
this assumption has infected all of our other systems.
This is no surprise: people tend to copy the models they have been
exposed to, and the model that most programmers are exposed to is
either Unix or something inspired by Unix.

But Unix uses ACLs (Access Control Lists), and ACLs are
[[][fundamentally broken]].
In no way do Access Control Lists follow the
[[][Principle of Least Authority (PoLA)]], which is necessary for users
to be able to sensibly trust their computing systems in this modern

599 600 601 602
To be sure, we need identity verification when it is important to know
that a certain entity "said a particular thing", but it is important
to understand that this is not the same as knowing whether a
particular entity "can do a certain thing".
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed

Mixing up identity verification with authorization is how we get ACLs,
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
and ACLs have serious problems.

For instance, consider that Solitaire (Solitaire!) can steal all your
passwords, cryptolocker your hard drive, or send email to your friends
and co-workers as if it were you.
Why on earth can Solitaire do this?
All the authority it needs is to be able to get inputs from your
keyboard and mouse when it has focus, draw to its window, and maybe
read/write to a single score file.
But Solitaire, and every other one of the thousands of programs on
your computer, has the full authority to betray you, because it has
the full authority to do everything you can... it /runs as you/.

And that's not even to mention that ACLs are subject to the same
confused deputy problems as discussed in the previous section.
In this paper we'll lay out how ocaps can accomplish some amazing
things that ACLs could never safely do... because [[][ACLs Don't]].

623 624
*** Content-centric filtering

625 626
When spam began to become a serious problem for email, Paul Graham
wrote a famous essay called [[][A Plan for Spam]].
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
The general idea was to use content filtering, specifically Bayesian
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
algorithms, to detect spam.
At the time of this article's release, this worked surprisingly well,
with the delightful property that spammers' own messages would
themselves train the systems.

Fast forward many years and the same fundamental idea of content
filtering has gotten much more advanced, but so have the attacks
against it.
Neural networks can catch patterns, but also can also increasingly
generate hard to detect forms of those same patterns, even generating
[[][semi-plausible stories]] based off of short prompts.
While most spam sent today is sent using what we might call "amateur"
methods, possible sophisticated attacks are getting worse and worse.

To add to this problem, false-negatives from these systems can be
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
644 645
[[][YouTube has marked non-sexual LGBT+ videos as "sensitive"]], and
many machine learning systems have been found to pick up
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
646 647 648
[[][racist assumptions]] from their surrounding environment
(and other forms of "ambient bigotry" from the source society's
power dynamics as well, of course).
649 650 651 652 653 654

This isn't to say that content filtering can't be a useful complement;
if a user doesn't want to look at some content with certain words,
they should absolutely free to filter on them.
But content filtering shouldn't be the foundation of our systems.

655 656
*** Reputation scoring

657 658 659 660 661 662 663 664 665 666 667 668 669
Reputation scoring, at the very least, leads us back to the problems
of high-school like pandering for popularity.
At the very worst, it results in a credit system that is
[[][disproportionally racist]].

The effort to categorize people based on their reputation is of
increased interest to both large companies and large governments
around the world.
An ongoing initiative in China is named the [[][Social Credit System]].
The effect of a reputation hit can be [[][wide-spread]] and
We need to do better.

670 671
*** Going back to centralization

672 673 674 675
After reading all this, one might be tempted to feel like the
situation is hopeless.
Perhaps we ought to just put power back in the hand of central
authorities and hope for the best.
We will leave it to our readers to look back at the problematic
677 678 679 680 681 682 683
power structures that probably lead them to examine distributed
social networks in the first place to see why this won't work.
But at the very least, companies don't have a good history of
standing up for human rights; if the choice is between doing
business in a country or not violating its citizens' rights,
most companies will seek to maximize value for their shareholders.

684 685
** A way forward: networks of consent

Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
686 687 688 689 690 691 692 693 694
Don't give up hope!
There is a way out of this mess, after all.
It lies with the particular use of a security paradigm called "object
capabilities" (or "ocaps").
We will get into the technicality of how to implement ocaps in
[[*How to build it][How to build it]] but for now, let's think about our high-level goals.

The foundation of our system will rely on establishing trust between
two parties.
If Alice trusts Carol to be able to perform an action, she might
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
"give consent" to Carol.
However, giving consent to Carol is not necessarily permanent; Alice
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
has the tools to track abuse of her resources, and if she sees that
Carol is irresponsible, she can revoke her consent to Carol.
(While Carol could have handed this authority to someone else, Alice
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
701 702 703 704
would still see the abuse coming from the access she handed Carol,
and could still hold Carol responsible.)

What about users that do not yet trust each other?
705 706 707
If Alice does not yet know or trust Bob, it is up to Alice's default
settings as to whether or not Bob has any opportunity to message Alice.
Maybe Alice only gets messages from entities she has existing
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
708 709
relationships with.

However, it is possible that Alice could have a "default profile"
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
that anyone can see, but which bears a cost to send a message through.
Perhaps Bob can try to send a message, but it ends up in a moderator
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
Or, perhaps Bob can send Alice a message, but he must attach "two
postage stamps" to the message.
In this case, if the message was nice, Alice might refund Bob one
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
717 718 719
or both stamps.
She might even decide to hand him the authority to send messages to
her in the future, for free.

But say Bob is a spammer and is sending a Viagra ad; Alice can keep
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
the stamps.
723 724
Now Bob has to "pay" Alice to be spammed (and depending on how we
decide to implement it, Alice might be able to keep this payment).
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
725 726 727
There is always a cost to unwanted messages, but in our current
systems the costs lie on the side of the receiver, not the sender.
We can shift that dynamic for unestablished relationships.
And critically, it is up to Alice to decide her threshold: if she is
729 730 731
receiving abusive messages, she can up the number of stamps required,
disable her public inbox entirely, or hand over moderation to a
trusted party during a particularly difficult period.
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
But even if she disables her inbox, the parties which have existing
trust relationships with Alice can still message her at no cost.
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed

735 736 737 738
While we do not claim that we can fully model a system of consent in
this system, we can provide the
/maximum amount of consent that is possible to represent/
in a system.
Hopefully that is far enough; it would certainly be better than what
740 741 742 743
we have now.

This document does not specify particular mechanisms but opens up
several opportunities.
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
744 745 746 747
It is up to the community to decide which routes are considered
But the framework for all of this is object capabilities.

748 749
** Must we boil the ocean?

750 751 752 753 754 755 756 757
All this sounds fine and well, but we are pressed with a problem: we
/already have/ ActivityPub implementations in the wild, and those
implementations filled in the holes in the spec in the best ways they
knew how.

We do not want to have to throw away the network we have.
As such, this document does not try to solve all possible problems.
For example, a Webfinger-centric interface is roughly incompatible
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
with Tor onion services, even if supporting such services would be
desirable and could be more easily accomplished with a [[][petname system]].
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
(Imagine trying to complete the Webfinger address of a user that
761 762
is using a v3 tor onion service address!)
As such, this document simply assumes the possibility that we will
Christopher Lemmer Webber's avatar
Christopher Lemmer Webber committed
live with Webfinger-based addresses for now, even if not optimal for
the long term.
765 766 767 768

Incremental improvement is better than no improvement at all, and
there is a lot we can make better.

* Understanding object capabilities (ocaps)

771 772 773 774 775 776 777 778
The foundation of our system will be object capabilities
In order to "give" access to someone, we actually hand them a
Ocaps are authority-by-possession: holding onto them is what gives
you the power to invoke (use) them.
If you don't have a reference to an ocap, you can't invoke it.

779 780 781 782 783 784 785
Before we learn [[*How to build it][how to build]] a network of consent, we should
make sure we learn to think about how an ocap system works.
There are many ways to build ocaps, but before we show the
(very simple) way we'll be building ours, let's make sure we wrap
our heads around the paradigm a bit better.

** Extending the car key metaphor
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802

We equated this to car keys before; the car doesn't care who you
are, it only cares that you turn it on using the car key.
(Note: though capabilities can also be built on top of cryptographic
keys, we're strictly talking about car keys for the moment.)

We can extend the car-key metaphor further, and find out there are
some interesting things we could do:

 - *delegation*: We could hand the car key to a friend.
   We could even make a copy for a trusted friend or loved one.
 - *attenuation*: We could hand out a car key that is more limited
   than the more powerful one we have.
   For example, if we go somewhere with full-service-parking, we can
   construct and hand the valet a "valet key" that only permits
   driving five miles and won't open the glove box or trunk.
   Sorry kid, you're not getting a joy ride this time.
803 804 805 806 807
 - *revocation*: Let's say that Alice wants to allow her roommate
   Bob to drive her car, but she also wants to be able to take away
   that right if Bob misuses it or if they stop being roommates.
   Alice can make a new car key that has a wire inside of it;
   Alice holds onto a device where if she presses the button, the
   key "self destructs" (ie the wire melts).
   Now Alice can stop Bob from being able to drive the car if she
   wants to (and if she does, it'll also disable access to anyone
811 812
   else Bob has delegated a key to).
 - *accountability*: Alice has multiple roommates, and while she would
813 814 815
   like to allow them all to drive her car, the next time someone
   spills a drink in the car and doesn't clean it up, she wants to know
   who to blame by seeing who drove the car last.
   Alice can achieve this via *composition*: she installs a separate
817 818 819 820 821 822
   "logging" panel into the dashboard of her car, to which she has the
   capability to view the logs.
   Next, for the keys that she hands her roommates, she composes together
   access to drive the car with the logging service and associates each
   key with her roommate's name.
   Now each time one of her roommates uses one of these keys, the logging
823 824
   console (which only Alice has access to) takes note of the associated
   name, so Alice can check who left a mess last.
825 826 827 828 829 830 831 832 833 834
You may have noticed that as we went further in the examples, the
ability to construct such rich capabilities on the fly seemed less
aligned with the physical car key metaphor (not that it wouldn't be
possible, but certainly not with such ease, and certainly not with any
cars that exist today).
And yet ocap systems easily do give us this power.
We will take advantage of this power to construct the systems we want
and need.

835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
** Ocaps meet normal code flow

The nice thing about object capabilities is that they permit the
"Principle of Least Authority": we can hand only as much authority
as is needed to complete a task.

We saw this earlier, in contrast to solitaire.
If we thought about solitaire as being a procedure, in contemporary
operating systems, running it would look like so:

#+BEGIN_SRC javascript
// Runs with the full "ambient authority" of the user.
// We don't need to pass in permissions, because it can already
// do everything... but that includes crypto-lockering our
// hard drive, uploading our cryptographic keys and passwords,
// and deleting our data.

By contrast, the ocap route would look like so:

#+BEGIN_SRC javascript
// We explicitly pass in the ability to read input from the
// keyboard while the window has focus, to draw to the window,
// and to read/write from a specific file.
solitaire(getInput, writeToScreen, scoreFileAccess);

In fact, Jonathan Rees showed that
[[][ocaps are just everyday programming]], and with a few adjustments
programming languages can be turned into ocap systems.[fn:whats-an-object]
(For example, instead of modules reaching out and grabbing whatever
they want to import, we can explicitly pass in access just as we pass
in arguments to a function.)

This may seem overwhelming.
How do ocaps get to all the right places then?
Thinking about ocaps as normal programming flow suddenly makes things
make more sense: how do values that come from all the way on /that/
side of our program get all the way to /this/ side of our program?
It turns out that most data passed around in programs doesn't come from
ambient user environments or global variables, it comes from normal
argument passing.
This should help us increase our confidence that we can use ocaps without
them being a burdensome part of our programming workflows; they are,
in fact, very similar to how we program every day.

[fn:whats-an-object] You may have been wondering, does the word
  "object" in "object capabilities" mean that our programs have to be
  particularly "object oriented"?
  First of all, no, ocaps can be implemented in what contemporarily
  may be perceived as very non-OO functional systems.
  Second, [[][object oriented]] is a very vague term.
  Third, "object capabilities" used to be just called "capabilities",
  but other computing systems started to implement things which they
  called "capabilities" which have nothing to do with the original
891 892 893
  definition of "capabilities" (eg, Linux's "capabilities" are closer
  to ACLs than they are ocaps), so the name "object capability" was
  chosen to distinguish between the two.

895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
** Ocaps meet social relationships (or: just in time authority)

Some systems that try to confine authority today are called "sandboxes".
If you have had experience with present-day sandboxes, you might be
skeptical, based on those experiences, that an ocap type system will work.
That would be understandable; in many such systems a user has to
pre-configure all the authority that a sandboxed process will need before
the process even starts up.
Almost inevitably, this authority doesn't end up being enough.
Time and time again, the user opens the sandboxed process only to find that
they have to "poke another hole" in the system.
Eventually they let too much authority through; out of frustration, the
user might simply pass through nearly everything.

Thankfully, ocaps don't have this problem.
Unlike many traditional sandbox systems, we can pass around references
whenever we need them... authority can be handed over "just in time".

This is less surprising if we consider the way passing around ocap
references resembles the way people develop social relationships.
915 916
If Alice knows Bob and Alice knows Carol, Alice might decide it is
useful to introduce Bob to Carol.
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
We see this all the time with the way people exchange phone numbers
"Oh, you really ought to meet Carol!  Hold on, let me give you her


/One of the Granovetter Diagrams shown in [[][Ode to the Granovetter Diagram]]./
/Pardon the geocities-era aesthetic./

In fact, thinking about such social relationships have long been at
the heart of ocap systems.
One of the most famous (and informative) ocap papers is one called
[[][Ode to the Granovetter Diagram]] (a truly remarkable paper which shows
how many complicated systems, including basic money and financial
transaction infrastructure, can be modeled on ocaps).
In this paper "Granovetter Diagrams" such as the above are introduced,
showing how ocaps flow through a system by social introductions.
In fact the above diagram is pretty much exactly the same as our phone
938 939
number exchange... "Alice is sending Bob the message =foo=, which
contains a reference to Carol, and now Bob has been introduced to /
940 941 942
has access to Carol."

It turns out that Granovetter Diagrams have their origin in sociology,
from a famous paper by Mark Granovetter named
944 945 946 947
[[][The Strength of Weak Ties]].
It's good news that much of thinking about ocaps has been based on
how human relationships develop in sociology, since we are now about
to use them to build a robust social network.
948 949

* How to build it

951 952
** Ocaps we can use in our protocols

953 954 955 956 957 958 959 960
Now that we have the good mental structure to think about how to build
this system, we need to assemble the pieces.
Since ocaps are the foundation, we need to think about how to represent
Note that there are many more ways to represent ocaps than the
mechanisms we are presenting; these have been chosen for their
simplicity and ease of implementation.

961 962
*** Ocaps as capability URLs

963 964 965 966
The easiest and simplest way to implement ocaps would be to use
simple but statistically unguessable "Capability URLs".
For example:

968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992

To have the address both brings you to the corresponding object
and gives you access to it.
(Or, in ocap terms, a capability URL "does not separate designation
and authority".
Most, and certainly the easiest to implement, ocap systems follow
this pattern.)
For such capability URLs to work, both sharing and accessing them
must be done through secure channels.

You may have seen these yourself before.
For example, on Google Docs there is the option to share the document
(and select whether you'd like to give full read/write access or just
read access) by sharing a URL.
Now you can just copy that URL to a friend and they have access.

Capability URLs are very easy to make and work with.
Unfortunately, there are a lot of [[][careful considerations]] one must
make when using capability URLs.
Most of these come from a conflict of expectations when working with
contemporary web browsers and web servers: by default, browsers leak
"where you came from" to "where you went to" via the =Referer= http
header (very dangerous when knowing where you came from gives you
additional power!) and servers log URLs to commonly insecure access
993 994

*** Ocaps as bearcaps

996 997
One way we might improve this situation is to use [[][bearcaps]].
Here's one that's roughly equivalent to the previous one:

: bear:?u=https://social.example/obj&t=sXJ9WWj6LRLCggZrjzfaeDutb8352OqSR0m2yg8XBkA

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
Bearcaps are very similar to capability URLs in a sense; they also
don't separate designation from authority, but that's because they
glue it together in two pieces:

 - *the =u= query parameter*: The URL to make requests against
   (in this case, =https://social.example/obj=)
 - *the =t= query parameter*: The bearer authorization token to be
   used when making this request (in this case,

This is then used to make a request:

#+BEGIN_SRC text
  GET /obj HTTP/1.1
  Host: social.example
  Authorization: Bearer sXJ9WWj6LRLCggZrjzfaeDutb8352OqSR0m2yg8XBkA

Note that in this case, the URL doesn't actually tell you what object
you're referencing: in this particular usage, the bearer token
actually is responsible for both pieces of designation and authority.
1022 1023 1024

**** What this doesn't prevent (conflicts with browser assumptions)
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
We've successfully moved our secret designator someplace that web
browsers and web servers will be less likely to leak data.
However, we should be careful to express limitations around what
we have just described.

The primary limitation with bearcaps, as with capability URLs,
actually comes from contemporary tooling.
While it would be possible to design a browser built with ocap
assumptions, contemporary browsers have not been built this way.
This leads to a couple of risky mismatches:

# - transparent or opaque? shoulder-surfing
# - slurp-it-up javascript

 - The first is that we are opened to "shoulder surfing" attacks.
   Imagine you are visiting or hovering over a page with a capability
   URL and someone took a photo of you; they can now type-in by hand
   that URL and gain its authority.
 - Second, any javascript that is loaded can scrape the page and
   gain access to all your capability URLs or bearcaps.
   [[][This has happened]], and arguably happens every day for most people;
   services like Google Analytics operate by "watching over the user's
   In a sense, we can see that this is the same attack as above, but
   for code supplied by a webpage or extension.

The solutions to these are similar.
It is unlikely that we can change the assumption that for URIs using
the =http:= or =https:= schemas that we can change browser behavior.
However, browsers do not even accept or know how to use the =bear:=
URI scheme.
In its standardization, we could specify a requirement that clients
treat =bear:= URLs as opaque.
For example, in response to the first of the two problems we
identified above, we could demand that the "full" bearcap not be
exposed (exposing the URL component might be fine) without an explicit
action (such as right-clicking on the link and saying "expose link").

The solution to the second problem is very similar once we realize
that browsers made the perimeter-security-is-eggshell-security mistake.
(And now we understand why dealing with CORS headers is such a
Except... "solving" this problem would mean brining explicit ocap type
security to the web in general, meaning that extensions could not
automatically reach in and scrape an entire page by default, for
We might be able to create a wrapper around solitaire, but fixing the
current generation of webpage and associated javascript deployment
assumptions is a migraine of the scarcely-possible.

However, there's good news: there are plenty of uses of the web which
are not just "contemporary web browsers" in the usage of APIs.
We can still use either capability URLs or bearcaps for endpoints which
are specifically for API endpoints as opposed to links that are intended
for human viewing.

1081 1082
*** Ocaps meet ActivityPub objects/actors

1083 1084 1085 1086 1087 1088
Finally we can get to the point of understanding how to apply ocaps to
ActivityPub directly.
It's actually extremely trivial: we can just use traditional capability
URLs or bearcaps anywhere we would put a normal link.

Let's start with a simple capability URL example.
(We're intentionally paring down this example; signatures are
1090 1091 1092 1093
not shown.)

#+BEGIN_SRC javascript
  {"@type": "Create",
1094 1095
   "actor": "https://chatty.example/bob/",
   "to": ["https://chatty.example/bob/followers"],
1096 1097 1098
   "object": {
     "@id": "https://chatty.example/obj/fQFWD9bZf1GKc3E09gt8W4MlChVxoiMAjgzhqxP9KhE",
     "@type": "Note",
     "attributedTo": "https://chatty.example/bob/",
1100 1101 1102 1103 1104 1105
     "content": "Hello, fediverse!  I'm new here.  Who should I be chatting with?"}}

There is no way to retrieve this object unless you know its address,
but knowing its address allows you to see/refer to it, not unlike the
Google Docs example we referred to earlier.
Bob distributes this message to his local friend group of Alice and
1107 1108

Alice thinks that Bob would like to meet her friends and composes
1110 1111 1112 1113
a reply which refers to his message.

#+BEGIN_SRC javascript
  {"@type": "Create",
1114 1115
   "actor": "https://social.example/alice/",
   "to": ["https://social.example/alice/collections/my-friends"],
1116 1117 1118
   "object": {
     "@id": "https://social.example/obj/Aj1k_Phx4uAgCXOMZ7KP9omJXXnOUySlhP-WXYE0obw",
     "@type": "Note",
     "attributedTo": "https://social.example/alice/",
     "inReplyTo": "https://chatty.example/obj/fQFWD9bZf1GKc3E09gt8W4MlChVxoiMAjgzhqxP9KhE",
     "content": "Hey Bob!!  Welcome to the network.  I want to introduce you to my friends."}}
1122 1123

In the former message, Bob shared his message amongst his followers
(which maybe he curates).
1126 1127 1128 1129
In the latter message, Alice sent her message, which also provided
a path to Bob's, amongst her friends, encouraging people she knows
to establish a social connection with Bob.
Alice and Bob were able to coordinate to spread this communication
1130 1131
amongst people they trust, but it is not spread further than to
anyone who is explicitly handed access.
(It's possible that someone can share the information when Bob or Alice
1133 1134
asked them not to; 

1135 1136 1137 1138

This is mildly interesting, but things get much more interesting when
we realize that inboxes can also themselves be capabilities.

Alice is a member of a group of pixel art enthusiasts.
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152

#+BEGIN_SRC javascript
  {"@type": "Group",
   "@id": "bear:?u=https://groupchats.example/group&t=eQshu8RiJ-9ozh2GKRATXN5-J6dcBVf_AYSMrJ6UEzE",
   "url": "https://groupchats.example/group/public/pixel-artists",
   "name": "Pixel Art Enthusiasts",
   "inbox": "bear:?u=https://groupchats.example/group/inbox&t=eQshu8RiJ-9ozh2GKRATXN5-J6dcBVf_AYSMrJ6UEzE"}

This pixel art enthusiast group has a public page that anyone can
view at =https://groupchats.example/group/public/pixel-artists=.
However, not everyone who can see that URL has the authority to
post to the group.
At present, Alice has the authority to make posts to this group,
which automatically disseminates them to all members.
However, Alice cannot moderate the group (including its membership
1156 1157 1158 1159
She has limited access.

We will worry about how the limited access is accomplished in a
moment, but for the moment we can say that Alice posting to the
1161 1162 1163
group is as simple as referencing its [email protected]=:

#+BEGIN_SRC javascript
1164 1165 1166
  {"@type": "Create",
   // this is the @id of the above referenced Group object
   "to": ["bear:?u=https://groupchats.example/group&t=eQshu8RiJ-9ozh2GKRATXN5-J6dcBVf_AYSMrJ6UEzE"],
   "actor": "https://social.example/alice/",
1168 1169 1170
   "object": {
     "@id": "https://social.example/obj/cdWg7wv1mjrNf0C3vcxCjzPy3Z9tturSBv9_Ew8qe7E",
     "@type": "Note",
     "attributedTo": "https://social.example/alice/",
     "content": "Anyone tried out libresprite?  I hear it's a fork of the old FOSS branch of aesprite."}}
1173 1174

1175 1176 1177 1178
Now the group can forward this message to its subscribers.

Here's the interesting aspects of this:

 - Alice's access to write to the group can be unique to her.  We'll
1180 1181 1182
   see how this can happen in the next section.  This means that it's
   also easy for the list administrator to unsubscribe her: they can
   just revoke the capability.
 - As said before, the capability Alice has here only allows her to
   post messages, not do moderation.
 - !Each subscriber on the group has given the group a specific
1186 1187 1188 1189 1190 1191 1192 1193
   capability for their subscription.  That means that the messages
   should go through to them "for free" unless the user explicitly
   chose to revoke the capability (but they probably would have
   manually unsubscribed instead).

** Adding attenuation, revocation, accountability, and composition

That's all good and well, but even reading the above might hint that
we are missing some things.  *How* is Alice's capability limited to
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
posting but not administrating?  *How* can both the susbscribers and
the servers both know where messages are "coming from" / hold them
accountable, and also have the power of revocation?

We are, in effect, now back to [[*Extending the car key metaphor][Extending the car key metaphor]] but
without having explained how it works... but we know what we have
claimed, that it is possible to have *delegation*, *attenuation*,
*revocation*, *accountability*, and *composition*.
Thus far we have only shown how delegation works: it is easy enough
to copy around a capability url / bearcap.
How about the rest?

*** The power of proxying

1209 1210 1211
It turns out that one abstraction gives us all the power we need:
simple, humble, everyday proxies.

Let's say Alice has a file on a file storage server corresponding to a
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
list of people she would like to invite to a party.
It is going to be an enormous birthday bash, so she decides she needs
to keep track of the participants:

: # aka <PARTY-FILE>
: https://filestore.example/obj/30SVLFRf1cTPNnjgaJfN8r85joIMVDSgWSKXKoYiFuY

Now she wants to collaborate with her friends about who can come to
the party.

**** Attenuation

This file object can accept any of the following two methods:

 - *=READ=*: Allows you to see who is currently on the list.
 - *=WRITE=*: Allows you to completely replace the file.

Alice would like to give Bob, Carol, and Lem access to see who is
1231 1232 1233 1234
on the list, but not to modify the list.
"If you want someone added to the list, you can call me up and
ask me to add them, but for now I'll just give you read access."

She makes separate read-only capabilities to give to Bob and Carol.
1236 1237 1238 1239 1240 1241 1242
These have the following addresses:

: https://filestore.example/obj/lRDWPHOvcbCrRsHc36ZeRcsIpDsunVFtwK3yxD1kH0c
: https://filestore.example/obj/DV9E9-jJaX7wFXtQuRMl1m_91d502cv-_5LX8F-GTn8

She hands these out to Bob and Carol respectively.

Now Bob tries making a =READ= request against =<PARTY-FILE-READ-ONLY-1>=.
It works!
Bob sees that one of his friends isn't on the list yet, so he tries
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
to =WRITE= a new copy of the file that has them listed.
Except that this time it throws an error.

As you may have guessed, =<PARTY-FILE-READ-ONLY-1>= is a proxy.
It knows where =<PARTY-FILE>= is, but does not share that information.
Instead, it forwards requests.
However, it only forwards requests for =READ=; any call to any other
method throws an error.
Due to the nature of object capabilities, this works!
Proxies turned out to be all that was necessary to add this
restriction on use.

Bob calls up Alice and tells her that he appreciates that she wants to
1262 1263
maintain the list, but he has a lot of ideas for people to add, couldn't
he please write to the file?
Alice decides that she completely trusts Bob to add new people to the list
1265 1266 1267 1268
but he has a bad habit of highlighting text while reading it and accidentally
deleting information.
But adding lines?  That seems ok.

Alice makes a new proxy to give to Bob:
1270 1271 1272 1273 1274 1275 1276

: https://filestore.example/obj/VhCCn5LjHKhDny50BIwCU8joyUgKyFIursNhfSgl1SY

This new proxy has a =READ= method along with a brand new method that
didn't even exist on the original object called =APPEND= which accepts
as an argument a single line to add to the file.
It was easy for Alice to build this: =APPEND= first does a =READ= against
1278 1279
=<PARTY-FILE>=, adds the line to those contents, and does a =WRITE= of the
new version.
Now Bob can =APPEND= as many guests as he wants, but there's no risk of
1281 1282
him deleting the current attendees.

1283 1284 1285
Carol hears of Bob's ability to append guests and is jealous.
She calls up Alice and says, can't I get access to =APPEND= also?
Alice trusts Bob to keep the number of guests he added within reason
1286 1287 1288 1289 1290 1291 1292
but she's not so sure about Carol.
She decides to make a new capability to give to Carol:

: https://filestore.example/obj/NSgn-DCUlbpe7DWTWipF92K09WFdfknYCpJnal0SgoQ

This supports the same kind of =APPEND= method interface as the
=<PARTY-FILE-READ-AND-APPEND-1>= that Alice gave Bob, but it has a new
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
restriction: this version of =APPEND= keeps track of an integer, the number
of guests added through that capability.
Once three guests have been added, it will refuse to allow any more
guests; any further calls to =APPEND= will throw an error.

It is amazing to consider that these powerful restrictions were able to
be added through proxying alone.
But we're not done exploring the interesting things we can do through
proxies just yet!

**** Revocation

Alice is up working late assembling the list when she gets a call from
Lem offering to help add people to the list.
Alice is very tired and says you know what, sure.
1309 1310 1311 1312 1313 1314 1315
She gives Lem the capability =<PARTY-FILE-READ-AND-APPEND-2>= and goes
off to bed.

She wakes up the next morning and the file is filled with all sorts of
nonsense attendees... most of them don't even have plausible sounding

It's so many additions that Alice knows at least that it couldn't have
been Carol; there were far more than 3 additions to this file.
It must have been Bob or Lem.
1319 1320
She calls both up in frustration.
Both swear they didn't do it!
Alice decides she doesn't have patience or time for this nonsense and
1322 1323 1324 1325
decides to cut off access before things get any worse.

Luckily she's well equipped to do so.
We left out a detail when we mentioned the previous capabilities that
Alice handed out... each one of them has an internal switch that can
1327 1328
be flipped (or, set a flag), at which point they will refuse to
forward messages anymore.
And the power to flip this switch is held by Alice and Alice alone:
1330 1331 1332 1333 1334 1335 1336 1337

: # has the power to set revocation flag of <PARTY-FILE-READ-AND-APPEND-1>
: https://filestore.example/obj/m2nRsjIPdUAl7puFR2tf6GnpGBhEF3KB3QXEWvRNVQQ
: # has the power to set revocation flag of <PARTY-FILE-READ-AND-APPEND-2>
: https://filestore.example/obj/trOC_8Ozfe1KxR8ZC32WcF_edk6dx3826uKslKdxvNI

Alice invokes both of them and poof!
1339 1340 1341 1342 1343 1344
The corresponding revocation flags are set.
now both refuse to forward messages.

**** Accountability

Both Bob and Lem contact Alice and insist neither of them made those
1346 1347 1348
edits to the document.
Couldn't they please get access again to write to the file?

That evening, Alice thinks about it and decides that yes, she could,
1350 1351 1352 1353
if next time she could hold whoever did it accountable so she could
prevent the problem from happening again and know who violated her

Alice makes two new capabilities, but these ones are a little bit
1355 1356 1357 1358 1359
different than before: while both allow writing to the file, this time
she associates each one with the name of the person she is handing it
out to.
Now if Bob writes to the file, it's logged that Bob made this change,
and if Lem writes to the file, it's logged that Lem made this change.
Alice hands out these new write-capable-but-logging ocaps to Bob and
1361 1362 1363 1364
Lem and logs off for the evening.

The next morning, the file is defaced again.
But the logger picks it up: Lem made all these changes!
Alice revokes the capability she gave to Lem and gives him a call