First Impressions - Privacy is key

Hi everyone,

Great work on the spec!
If some of my statements are wrong assumptions please do correct me!
A few things I would like to get clarified / have questions / concerns about:

Privacy. Payment relationships should not be broadcasted publicly. They should be retained and owned by the user (whether that is the payee and/or the payment receiver; I am open), they should be portable with supported platforms where the creator and/or the fan sign into.

Waiting for permissioned data is key for ensuring interoperability with the bsky app. But I would like to see implementations of private attestations that are user owned, portable built on OTHER solutions, other than PBC software. I am looking at Stratos (or a fork of Stratos) - used explicitly for adult content platforms and adult only communities.

As a former adult content creator, I do not desire to publicly announce who my fans are. Nor do I want to risk any of my fans to dox themselves by accident (this is about DIDs.. not gonna get into this here).

Moderation. Moderation is a key responsibility a platform takes on. Especially as an adult content platform, legal and regulatory requirements are increased.

What happens when a creator uploads illegal or copyrighted content, or the platform has to take down content due to requests from their payment service provider (PSP)? How does this spec ensure that all inter-operating platforms communicate via the spec? If the spec does not solve for this how can we build systems using Ozone, Osprey (et al.,) to ensure that the spec outlines clear moderation systems that support this purpose for spec-following platforms so that we can protect people from harmful or illegal content. And how do we kick out services that do not follow Moderation laws in an age where the ones supposed to be protecting people are the ones harming people. I don’t want a system that is permissionless. It needs to gate actors, if the system is permissionless a single bad actor could mess it up, if its gated, they can be off boarded.

No revocation primitive. Nothing in the lexicon says “this attestation is no longer valid as of T.” attested.network has no record type for that. (This is critical for moderation requirements, not a nice-to-have). A creator needs to be able to revoke/block fans that are harassing/harmful. How does this translate across the spec?

Recurring subscriptions. The world runs on subscriptions, the network.attested.payment.recurring records is a great start but what’s missing is (1) an expiration/validity-window primitive, and (2) a mid-cycle state-change primitive (to revoke a state for legal and moderation purposes).

Trial periods. “7 days free, then 10/mo”. Missing entirely.

Fee updates. What happens if the creator changes their fee from 5/month to 10/mo ?

Affirmative cancellation. There is no cancellation record. The spec doesn’t define a termination signal, which means verifiers have no positive way to know a subscription has ended vs. is temporarily delinquent vs. is still active and just between bills, vs. the fan has cancelled their subscription before being charged again (but still can access content until the billing period ends).

Attestations should either auto-expire, or have a timestamp or other type of mechanism for understanding when the fan needs to pay again. So that a platform can attest a payment was made on T. and is valid for T+1/7/30/365 days.

Malicious actors and security. How do you ensure that a malicious actor does not receive full-account permissions (which is easy to get by pretending to be an ethical new project) and starts deleting attestations and thereby severing the actual platforms experience that the fan and creator have established.

If your change from atprotofans to attestednetwork excludes adult content creators, I understand. Its a hard problem. If the change in brand does not exclude these groups I would be happy to get involved in this working group.

2 Likes

Thanks for the write-up @dave.self.surf!

Payment relationships should not be broadcasted publicly.

I think we agree on this with one small, but important change:

“Payment relationships should not only be broadcasted publicly.”

There are plenty of valid real-world use cases where this holds true. For example, organizations like the AT Protocol Community Fund (https://opencollective.com/atprotocoldev) operate publicly and transparently with regard to where and how they receive and spend funding. Another example would be a crowd-funded project where pledges and backers are publicly acknowledged and the “who and how much” is used to encourage and market as social proof.

Moderation is a key responsibility a platform takes on.

I’m not sure I see the connection to how this is part of the protocol, but rather specific to the implementation of brokers and AppViews.

This is out of scope, so I’m going to just move on from the topic.

No revocation primitive.

Ah, there’s a key part of the attention spec that is really relevant. The purpose of having the “signatures” field that references either remote or inline attestation proofs is exactly this revocation mechanism.

It’s described in detailed on the Payment Brokers — attested.network page in the “Invalidation” section.

To invalidate a payment, the broker deletes or updates its network.attested.payment.proof record and notifies the recipient. Apps that verify payments will see the proof is missing or marked invalid and treat the payment as no longer active.

The same holds true for either the broker or the recipient of payment. A creator, for example, can revoke their “proof” associated with the payment in whatever way they are contractually or technically allowed to, just as the broker can.

Recurring subscriptions.

Information like when a recurring payment (that may or may not be a subscription) are meta-data that can and should be included in the attestation record. This spec openly and flexibly allows different types of proofs. The important thing to note is that these are largely specific to the use case and AppViews, so this spec doesn’t try to prescribe the internals or implementation details of that part of the relationship.

For example, if I subscribe to 6 months of ad-free podcast content paid monthly I may have a record that looks like this:

{
  "$type": "network.attested.payment.recurring",
  "subject": "did:plc:podcast-creator",
  "amount": 1000,
  "currency": "USD",
  "unit": "monthly",
  "frequency": 1,
  "txnid": "01J6M4R5XQHV8WNBCM3G9RFBT",
  "createdAt": "2026-03-01T00:00:00.000Z",
  "entitlements": [
    {
      "$type": "com.atproto.repo.strongRef",
      "uri": "at://did:plc:podcast-creator/com.example.podcast.subscription/premium",
      "cid": "bafy...ekcc"
    }
  ],
  "signatures": [
    {
      "$type": "com.atproto.repo.strongRef",
      "uri": "at://did:plc:podcast-creator/network.attested.payment.proof/3la8rxz3vdc4t",
      "cid": "bafy...omfe"
    }
  ]
}

And the attestation held by the podcaster could have some additional information that is relevant to the subscription:

{
  "$type": "network.attested.payment.proof",
  "cid": "bafy...gjru",
  "validUntil": "2026-09-01T00:00:00.000Z",
}

This information isn’t really useful to the network, but it could be very useful to applications and AppViews that serve podcasts and know how to process this type of entitlement.

Affirmative cancellation

Asked and answered. I think you just overlooked this in the spec.

Malicious actors and security

Again, this isn’t really in scope of the spec. This is an issue that implementors of the spec have.

4 Likes

Thank you for the clarifications and corrections!

1 Like

@dave.self.surf Worth noting that the spec does specify the “payment servicer” (broker) could do private records, when that becomes available. While I agree with @ngerakines.me that there are valid use cases, I would expect that my company (as a broker) would likely default to private. (I haven’t dug into the Permissioned Data Spaces yet, so I’m not sure if that would/could be a decision for the payer/customer at the time of payment, or the creator/merchant when they configure their broker.)

Even for low-risk / non-adult purchases… as a customer, I’d think twice if I had to think about the privacy implications of any transaction being public.

3 Likes

there are valid use cases, I would expect that my company (as a broker) would likely default to private.

I agree with Nick regarding use-cases where public data is an inherently valuable part of the process itself. I also acknowledge that there are far more use-cases where the default should be non-public/private/permissioned.

Today, public payments data is the exception, and we should model after it because this is how the world works.

I haven’t dug into the Permissioned Data Spaces yet, so I’m not sure if that would/could be a decision for the payer/customer at the time of payment, or the creator/merchant when they configure their broker.

The Permissioned Data Spaces (is an ongoing exploration between Bluesky PBC and the ecosystem), dholms leaflet https://dholms.leaflet.pub started documenting their (Bluesky PBC) thinking around this (which I very much value being done publicly where they accept and improve with community feedback, eg., the namespace changed from Buckets to Spaces due to community feedback).

As much as I trust the capabilities and skills of the Bluesky PBC team, they have also promised E2EE messaging which never shipped (beyond a button integration to Germ). So we can only hope that they will release Permissioned Data Spaces this year, but I wouldn’t bet any horses on it.

That’s why I suggest we look into Stratos GitHub - NorthskySocial/stratos: Shared Private Data · GitHub which does what the app.bsky.* lexicon does (which is good enough for my use case).

Stratos is Northsky’s off-PDS permissioned data layer for atproto. Users enroll via OAuth, get a per-actor MST repo hosted on the Stratos service (not their PDS), and create private records scoped by “boundaries” (access-control tags, viewers need a matching boundary to read). An AppView*-facing indexer consumes per-user sync streams and filters content at query time based on the viewer’s enrolled boundaries.

AppView = backend/server btw (the name is very misleading just wanna clarify)

2 Likes