Closed staltz closed 1 year ago
Yeah that last one struck me too (sharing keys across devices - not ideal). nostr all over again?
We need sympathetic replication that
What info is known only to members of a group but noone else - the group secret. Could we obfuscate tangle root data by using that difference? We used this with cloaked msg ids (which were designed to provide a groupId which does not leak the initial message id, and hence author). That alone doesn't get us hidden association with group... unless you make a "per group-member" cloaked id.
Eg I call it group DD2E because that is hash(groupSecret, groupInitMsgId, accountId) And you publicly refer to it by something else because your accountId is different.
This works I think, but don't know what implications are for your replication.
why can't we do what we did with the "invitations" feed? (expect a bundle of add-member messages you can't open, but when you can it lets you read other add-members, hence discovering all the peers
Yeah that last one struck me too (sharing keys across devices - not ideal). nostr all over again?
This is not a big problem, in my opinion. We are already sharing a symmetric key. We can share an asymmetric keypair, as long as we use that keypair only for a specific purpose: signing internally encrypted msgs only.
My two cents. When the initial direction becomes too complex, it is sometimes worth resetting and looking in another direction.
Signal has proven to have very efficient and scalable group messaging. The basic principle does not rely on any global secret sharing, which makes it easy to manage membership (which is always a problem with globally shared secrets). All that is needed is a way for actual members to be able to check the membership changes, change their encryption key, and secretly publish it to other actual members.
The sole issue to replicate that is the fact that we do not have a central database holding the 'membership.' That is something that could be resolved using a CRDT OR-SET.
@gpicron Okay, where do see complexity? Sharing of keys isn't complex, in my opinion. The "encrypted msg inside another msg" of this attempt 2 is somewhat complex.
Signal has proven to have very efficient and scalable group messaging.
Signal isn't perfect and it's not a drop-in solution for this case. First, I dislike how Signal groups lose context for new group members, and that's a lost user feature, because we want to include new members and give them access to everything that has been said before. Second, from what I learned in the original notes by Dominic and Keks when they were designing private groups, is that double ratchet is not scalable for large communities. Third, Signal is operating with different assumptions, i.e. a centralized database from which peers can gain consensus. Other solutions like MLS are also designed with a centralized db in mind.
Third, Signal is operating with different assumptions, i.e. a centralized database from which peers can gain consensus
yea, my thought also when I read this. Signal is very different.
First, I dislike how Signal groups lose context for new group members, and that's a lost user feature, because we want to include new members and give them access to everything that has been said before
There is a simple solution for that with anchor mechanism that I proposed you and that fit well with PPPP, and each member can choose individual if they want the new member to see or not their previous messages in the group.
Second, from what I learned in the original notes by Dominic and Keks when they were designing private groups, is that double ratchet is not scalable for large communities.
Yes it cannot scale to tens of thousands but it can easily scale to a few thousands. Can the current approach do ? With respect to the in memory db constraint that you impose to ppppp ?
Third, Signal is operating with different assumptions, i.e. a centralized database from which peers can gain consensus. Other solutions like MLS are also designed with a centralized db in mind.
Yes this is the key point. But, they achieved to design a database that is storing zero-knowledge proof of membership and roles making it 'stealable'. A hacker stealing the db or even a Signal admin cannot determine who is part of the a group and who administer a group. So that "centralized membership database" can be in clear visible to everybody. And using CRDT we can perfectly do that using SSB feeds or pppp.
But, forget it. You don't like the idea... You drive. Have a good day
β How is group exclusion (i.e. epochs) structured? (see ssb-group-exclusion-spec)
I took the time to try to answer this carefully. @mixmix the following may interest you.
First I think I was wrong about the "account tangle" described in the OP. The account tangle does not need to encode the members of the group, it only needs to encode the "signature keys" for different epochs. So it should look more like this:
graph RL
msg0["declare GROUP_KEY1\nby GROUP_KEY1"]
msg1["declare GROUP_KEY2\nby GROUP_KEY1"]
msg2["declare GROUP_KEY3\nby GROUP_KEY2"]
msg2-->msg1-->msg0
(Recap: GROUP_KEY is used for signing messages published by this group, and for symmetric encryption for group content)
The group can spawn a new external feed that serves as a CRDT to maintain the set of members. The msgs in this feed can be encrypted to the group AND to the new member (or excluded member). In this way, this feed also serves as the additions feed. Example:
---
title: Members feed
---
graph RL
msg0[account=GROUP,\ndomain=members]
msg1["enc([GROUP_KEY1], {\nadd ALICE by ALICE\n}) by GROUP_KEY1\n\na"]
msg2["enc([GROUP_KEY1,BOB], {\nadd BOB by ALICE\n}) by GROUP_KEY1\n\na,b"]
msg3["enc([GROUP_KEY1,CARLA], {\nadd CARLA by BOB\n}) by GROUP_KEY1\n\na,b,c"]
msg4["enc([GROUP_KEY2,CARLA], {\ndel CARLA by ALICE\n}) by GROUP_KEY2\n\na,b"]
msg5["enc([GROUP_KEY3,CARLA], {\ndel CARLA by BOB\n}) by GROUP_KEY3\n\na,b"]
msg3-->msg2-->msg1-->msg0
msg5 & msg4-->msg3
If you're going to invite Fred to your group, he doesn't know your Group ID yet. And you don't have an additions feed for Fred to replicate, so how should this work?
domain="members"
he can replicate that feedOr, if Fred doesn't consent, then step 3 never happened, and the only thing Fred learned was the existence of GROUP_ID and its encrypted feeds.
Or, if a lot of time passed between step 2 and step 3, you can choose to not do step 3. In effect, cancelling the invitation (this was an important requirement that Nicholas identified).
I'm still going through this, so the following is a big work-in-progress, but I believe we can resolve the 4 different set scenarios (same membership, subset membership, overlapping membership, disjoint membership), but it'll be tailored for these account tangle and members feed structures. Here's an example:
---
title: Members feed
---
graph RL
msg0[account=GROUP,\ndomain=members]
msg1["enc([GROUP_KEY1], {\nadd ALICE by ALICE\n}) by GROUP_KEY1\na"]
msg2["enc([GROUP_KEY1,BOB], {\nadd BOB by ALICE\n}) by GROUP_KEY1\na,b"]
msg3["enc([GROUP_KEY1,CARLA], {\nadd CARLA by BOB\n}) by GROUP_KEY1\na,b,c"]
msg4["enc([GROUP_KEY2,CARLA], {\ndel CARLA by ALICE\n}) by GROUP_KEY2\na,b"]
msg5["enc([GROUP_KEY3,CARLA], {\ndel CARLA by BOB\n}) by GROUP_KEY3\na,b"]
msg3-->msg2-->msg1-->msg0
msg5 & msg4-->msg3
---
title: Account tangle
---
graph RL
msg0["declare GROUP_KEY1\nby GROUP_KEY1"]
msg1["declare GROUP_KEY2\nby GROUP_KEY1"]
msg2["declare GROUP_KEY3\nby GROUP_KEY1"]
msg3["redeclare GROUP_KEY2\nby GROUP_KEY1"]
msg1--"a excludes c"-->msg0
msg2--"b excludes c"-->msg0
msg3-->msg1 & msg2
winner["wins (lexicographically)"]-.->msg1
style winner fill:#0000,stroke-width:0
@staltz corrections:
I'll come back to the diagrams and tegh stuff when I'm not at noisy kids gymnastics.
But I think we should consider @gpicron 's suggestions about MLS. Those are strong and valuable claims. Even if we don't use it sounds like there is valuable prior art to check.
Reminder that the envelope spec and tribes came out at about the same time as MLS... so we didn't integrate ideas from that project in owr thinking so far.
@staltz I feel tension with @gpicron here. My take is that they don't feel heard, or what they're sying is seen as irrelevan? (I could be wrong) I notice I'm a bit annoyed by your innacurate history (you forgot I was there and helped design this, or didn't know? And you got details wrong ... I think). I don't care about that that much (we have a good relationship and I know you update your models on feedback). What I do care about is "how do you want ppppp to grow and be governed?" I want it to have a few strong and committed parents guiding and growing it into maturity. This is not a calling out, it's a calling in (death by cheesy) - to invite us to consider, "can we align for this to be a shared project". The alternative is "this is staltz's baby and we are just advisors". That is totally valid too. Maybe I'm just asking to clarify that. (with love :heart:)
(I'm just jumping in here making random comments, with the intention of making discussions like this one more discoverable. If my comment is out of scope, please just hide it as such.)
[β¦] Last I checked the only problem I surfaced was that the [MLS] spec does not have a distributed key cycling process for new epochs (yet). [β¦]
related ticket documenting this problem: https://github.com/mlswg/mls-federation/issues/6
I want to get the conversation back on track, which is "how to accommodate SSB's private message model to PPPPP". Let's have other conversations to discuss other issues, especially the higher-level ones like "how do you want ppppp to grow and be governed?". Not in this issue.
I am ruling out Double Ratchet and MLS and anything else like that simply because it is a research effort (distributed systems and cryptography is no picnic, and it usually takes real academic effort) and/or implementation effort for which we have no resources yet. Hopefully one day we'll have the resources (time, money, team, reviewers) to do all that properly. What we have resources for now is to port existing SSB mechanisms which β despite crippled β are implemented and are proven to work well enough between users. As a metaphor, PPPPP is a new house, and we're just moving furniture from the SSB house to this one, and we need to see how to place the furniture in the best places. We are NOT buying new furniture, and we're definitely not building furniture ourselves. So can we focus on how to move furniture? Just picking things up and putting them where they make sense.
@mixmix with all that said, I want to see what you think about those diagrams and since you're one of the few with loaded context on private groups and metafeeds, whether it seems like the "exclusion spec" furniture could fit in this new "accounts and tangles" bedroom.
More technical comments:
Encryption scheme: Given the flexibility of envelope-spec to have all sorts of recipients, and that both ssb-box1 (private-box) and ssb-box2 (envelope-spec) are based on the same libsodium.crypto_secretbox_easy
primitive, I think there's no point in doing private-box, we should just use envelope-spec for all purposes. This will simplify code too. However, I think I'll have to adapt some minor details of envelope-spec e.g. it assumes feed_id
and prev_msg_id
are available information. Works for SSB but not for PPPPP.
Validation complexity: The idea of embedding a {data,metadata,pubkey,sig}
msg inside a msg.data
seems like it will work well, but it's going to make implementation of the DB module a lot more complex. This is also the only unanswered question so far from the list of open questions I posted in the OP. Recap:
β What if the inner msg has tangle information too? (In fact it should, because each group member needs to tangle their msgs together, to assert order of their msgs) How to construct the inner tangle, what msg IDs do we use for that? How to validate the inner tangle?
Key management: I'm not sure if there should be a symmetric key for internal communication. What if there is only a "internal keypair" which is used for both signing the outer msg and encrypting/decrypting the inner msg (once the ed25519 keypair is converted to curve25519)?
Here's an early attempt at specifying the outer msg (containing the inner msg in data
):
{
data: encrypted([GROUP_KEY, BOB], {
data: {
// ... add Bob to the group
},
metadata: {
account: ALICE,
// accountTips,
// dataHash,
// dataSize,
domain: "members",
tangles: {
[ALICE_MEMBERS_FEED_ROOT]: {
// ...
// msg IDs here are inner msg IDs
}
}
},
pubkey: ALICE_DESKTOP,
// sig,
})
metadata: {
account: GROUP_ID,
// accountTips,
// dataHash,
// dataSize,
domain: "members",
tangles: {
[MEMBERS_FEED_ROOT]: {
// ...
// msg IDs here are outer msg IDs
}
},
v: 3,
},
pubkey: GROUP_KEY,
// sig,
}
Key management: I'm not sure if there should be a symmetric key for internal communication. What if there is only a "internal keypair" which is used for both signing the outer msg and encrypting/decrypting the inner msg (once the ed25519 keypair is converted to curve25519)?
would there be a performance impact to using an asymmetric keypair for encryption and decryption? my intuitive understanding is that asymmetric keypairs are less performant, hence why are usually used to bootstrap a symmetric key. i understand the appeal to having less keys to manage, but at the same time i think why use an asymmetric key pair for encryption / decryption when all parties have both sides of the key pair? also as an aside, i don't think this would have an impact due to the signing, but it also means anyone (who isn't a member) could encrypt a message using the public key (but they wouldn't be able to sign such a message using the private key).
@ahdinosaur That's a strong point, thanks for the reminder! And yeah, besides the performance and misuse by strangers, perhaps it's better to have the asymmetric keypair only for signing, and the symmetric key only for encryption. Case closed
Thoughts:
GROUP_KEY
in the two ways you've describedI think you mean
GROUP_SECRET
- a symmetric key for encryptionGROUP_PK
- the public part of a keypair used for verifying the signature. Currently, when "bob" is included in the recps, that means "make a shared key by crossing the authors SK with bob's PK". Then when bob receives this he knows he can try a shared key (which he makes from his SK and the authors PK). Buuutt, who is the author here? msg.metadata.account = GROUP_ID
... this then requires an encryption key for the group ... is that GROUP_PK (ed25519)
converted into GROUP_PK (curve25519)
?
As you know, I like the idea of being able to cycle keys... it may be a requirement for groups which support exclusion even?
@mixmix Yes, you're right in those details.
A group should declare:
GROUP_INTERNAL_KEY
A symmetric key for encryption (obviously this is only shared in secure channels)GROUP_PUBKEY
Public part of ed25519 keypair used for signaturesGROUP_EXTERNAL_KEY
Public part of curve25519 keypair used for Diffie-helmann and thus direct messagesAs you know, I like the idea of being able to cycle keys... it may be a requirement for groups which support exclusion even?
Yes and yes.
I found a problem that could be a deal breaker.
With the wrapping idea, it seems like we cannot support deletion of the inner msg.data
and (thus) partial replication. It seems like deleting the inner msg.data
will change the ciphertext, and thus change the outer msg.metadata.dataHash
and thus the outer msg ID and sig. Partial replication, i.e. sliced replication of a minDepth
/ maxDepth
range of the tangle is a huge requirement for PPPPP and if groups cannot support it, we'll be in trouble quickly, regarding storage usage.
The only other alternative I have in mind is leaving the author's information on the encrypted msg, i.e. group content msgs would look like this:
{
data: encrypted([GROUP_KEY, BOB], {
// ... add Bob to the group
}),
metadata: {
account: ALICE,
// accountTips,
// dataHash,
// dataSize,
domain: hash(GROUP_ID+"members"),
tangles: {
[ALICE_MEMBERS_FEED_ROOT]: {
// ...
// msg IDs here are inner msg IDs
}
}
},
pubkey: ALICE_DESKTOP,
// sig,
}
So whoever (group outsider) gets a hold of this msg will know that it belongs to Alice, but they don't know which group it belongs to, since the GROUP_ID
is hashed. Sympathy replication would still work because given account=ALICE
& domain=hash(GROUP_ID+"members")
you would know the moot ID and thus can fetch the whole feed for Alice's additions of members to this group, without knowing (as a group outsider) what these msgs are actually about.
The downside is that we can't do a "pruning CRDT" (see thesis) of the membership set, because there isn't a single feed for that, there is Alice's membership-mutation msgs, Bob's membership-mutation msgs, etc. In each of these feeds we can do pruning, but the opportunities for that are rare, since we can't "refactor" the membership set like we can if we had one feed.
So in essence, partial replication here is also damaged.
Oh, wait!
I think the wrapping idea could still work with partial replication, we just need to delete the outer msg.data
, then the outer msg.metadata
is preserved etc.
Regarding tangles in the inner msg, I'm considering leaving it totally empty. Basically the inner msg would be "orphan", it doesn't belong to anything.
It makes sense when you think of the ultimate purpose of msg.metadata.tangles
, which is to assert what msgs came after what, an ordering guarantee. If we already have an ordering guarantee from the outer msg tangle, we don't need a second ordering guarantee. What the outer msg tangle does is provide or ordering guarantee across many accounts participating in the group, while the inner msg provides ordering guarantee for a single account. Seems like the outer tangle is stronger and we don't need the inner one.
Alright, I think I settled on a design, and for reference I'll (re)describe it in this comment, to centralize the ideas.
A group account needs three types of keys:
The account tangle has:
GROUP_SIGKEY
pubkeyGROUP_EKEY
pubkeyGROUP_SIGKEY
, GROUP_IKEY
, GROUP_EKEY
are issuedExample:
graph RL
msg0["GROUP_SIGKEY1\ndeclares\nGROUP_SIGKEY1"]
msg1["GROUP_SIGKEY1\ndeclares\nGROUP_EKEY1"]
msg2["new epoch!\n\nGROUP_SIGKEY1\ndeclares\nGROUP_SIGKEY2"]
msg3["GROUP_SIGKEY2\ndeclares\nGROUP_EKEY2"]
msg4["..."]
msg4-->msg3-->msg2-->msg1-->msg0
Maintains the set of members. The account=GROUP_ID
(msg ID of the root of the account tangle) and domain="members"
. The msgs in this feed are encrypted (symmetrically) to the group and (asymmetrically, Diffie-Hellman) to the new member (or excluded member).
The msg.data
is yet another msg, called the "inner msg", where innerMsg.pubkey
and innerMsg.metadata.account
refer to a specific member of the group. Contrast that with the (outer) msg.pubkey
which is GROUP_SIGKEY
and msg.metadata.account
which is GROUP_ID
. The inner msg does not need to have tangles, i.e. innerMsg.metadata.tangles = {}
, but the outer msg needs to be tangled as the members feed.
For partial replication, the inner msg can be deleted entirely, leaving the outer msg.data
to be null.
Example:
graph RL
msg0[moot\n\naccount=GROUP_ID,\ndomain=members]
msg1["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1], {\nALICE adds ALICE\n})\n\na"]
msg2["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1,BOB], {\nALICE adds BOB\n})\n\na,b"]
msg3["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1,CARLA], {\nBOB adds CARLA\n})\n\na,b,c"]
msg4["GROUP_SIGKEY2\npublishes\nenc([GROUP_IKEY2,CARLA], {\nALICE excludes CARLA\n})\n\na,b"]
msg5["GROUP_SIGKEY3\npublishes\nenc([GROUP_IKEY3,CARLA], {\nBOB excludes CARLA\n})\n\na,b"]
msg3-->msg2-->msg1-->msg0
msg5 & msg4-->msg3
New comers are "invited" to replicate and decrypt the members feed.
GROUP_IKEY
(secret), GROUP_SIGKEY
(private) and GROUP_EKEY
(private)GROUP_ID
, and based on that plus the domain="members"
he can replicate the members feedOr, if Fred doesn't consent, then step 3 never happened, and the only thing Fred learned was the existence of GROUP_ID and its encrypted feeds.
Or, if a lot of time passed between step 2 and step 3, you can choose to not do step 3. In effect, cancelling the invitation (this was an important requirement that Nonlinear identified).
I'm still going through this, so the following is a big work-in-progress, but I believe we can resolve the 4 different set scenarios (same membership, subset membership, overlapping membership, disjoint membership), but it'll be tailored for these account tangle and members feed structures. Here's an example:
---
title: Members feed
---
graph RL
msg0[moot\n\naccount=GROUP_ID,\ndomain=members]
msg1["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1], {\nALICE adds ALICE\n})\n\na"]
msg2["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1,BOB], {\nALICE adds BOB\n})\n\na,b"]
msg3["GROUP_SIGKEY1\npublishes\nenc([GROUP_IKEY1,CARLA], {\nBOB adds CARLA\n})\n\na,b,c"]
msg4["GROUP_SIGKEY2\npublishes\nenc([GROUP_IKEY2,CARLA], {\nALICE excludes CARLA\n})\n\na,b"]
msg5["GROUP_SIGKEY3\npublishes\nenc([GROUP_IKEY3,CARLA], {\nBOB excludes CARLA\n})\n\na,b"]
msg3-->msg2-->msg1-->msg0
msg5 & msg4-->msg3
---
title: Account tangle
---
graph RL
msg0["GROUP_SIGKEY1\ndeclares\nGROUP_SIGKEY1"]
msg1["GROUP_SIGKEY1\ndeclares\nGROUP_EKEY1"]
msg2["GROUP_SIGKEY1\ndeclares\nGROUP_SIGKEY2"]
msg3["GROUP_SIGKEY1\ndeclares\nGROUP_SIGKEY3"]
msg4["GROUP_SIGKEY1\nredeclares\nGROUP_SIGKEY2\nand deletes\nGROUP_SIGKEY3"]
msg1-->msg0
msg2--"a excludes c"-->msg1
msg3--"b excludes c"-->msg1
winner["wins (lexicographically)"]-.->msg2
msg4-->msg2
msg4-->msg3
style winner fill:#0000,stroke-width:0
For figuring out which epoch to prefer, we need the group membership set, and at a certain state, so it seems like we will need to know the tips of the members feed declared in each account msg, with the exception of the first account msg.
msgOnAccountTangle = {
data: {
// ... add a new SIGKEY
membersTips: [/* ... */], // msg IDs of the current tips of the members feed
},
metadata,
pubkey,
sig,
}
well done @staltz, very exciting :relaxed:
With the wrapping idea, it seems like we cannot support deletion of the inner
msg.data
and (thus) partial replication. It seems like deleting the innermsg.data
will change the ciphertext, and thus change the outermsg.metadata.dataHash
and thus the outer msg ID and sig. Partial replication, i.e. sliced replication of aminDepth
/maxDepth
range of the tangle is a huge requirement for PPPPP and if groups cannot support it, we'll be in trouble quickly, regarding storage usage.
with Zen of Python "flat is better than nested" in mind, could an alternative approach be to not nest the encrypted data
and metadata
. so data
is still data
, now encrypted, and groupMetadata
(with the group's inner metadata
, pubkey
, and sig
), also encrypted.
For completion, here's how private messages (between e.g. Alice and Bob) would work:
With new devices or new apps:
with Zen of Python "flat is better than nested" in mind, could an alternative approach be to not nest the encrypted
data
andmetadata
. sodata
is stilldata
, now encrypted, andgroupMetadata
(with the group's innermetadata
,pubkey
, andsig
), also encrypted.
@ahdinosaur I'm not sure if that can be done without making msgs have a dynamic shape, which is also kind of bad. I'm trying to go for "static shapes are better than dynamic shapes" and currently the shape we have is:
{ data, metadata, pubkey, sig }
,
and we only have freedom inside data
.
The discussion can continue, but I want to signal that I found a solution. Closing!
Problems with attempt 1
My first design of private groups (symmetric key group with many members) assumed that we could put all members in an account, and then group activity is just an "internal feed" (a feed published by the account, where every
msg.data
is encrypted with the symmetric key).The main problem with this is that the account tangle would leak what the members of the group are, and whenever a content msg was published on the internal feed, we would also know what member is publishing.
Requirements
So let's restart this by highlighting what we want.
Notation
CLICK HERE: Short-form for msgs
Whenever I say `data=DATA metadata={ account=A, domain=D } pubkey=PK` it will represent the short form of: ``` { data: DATA, metadata: { dataHash: ..., dataSize: ..., account: A, accountTips: ..., domain: D, tangles: { ... }, v: 3, }, pubkey: PK, sig: ... } ```
Attempt 2
π‘ Embed a second "metadata" inside the encrypted
msg.data
to achieve membership privacy.π‘ We can do better! Let's embed an ENTIRE msg inside
msg.data
!Account tangle
Typically, an account works like this: (msg0:) device1_pubkey signs a msg that declares itself in
msg.data
, then (msg1:) device1_pubkey signs a msg that declares device2_pubkey inmsg.data
.What occurred to me is that there could be a "master" pubkey which is shared to all the peers in the account, such that any peer in the account can use that to sign messages. This will gain us membership privacy.
During onboarding a.k.a. Hug :people_hugging: (of a device into the "person" account, OR of a new member into the "group" account), the following information can be exchanged privately, outside of the gossip/replication system:
IN_KEY
used forInEnc
GROUP_PUBKEY
)Here's how the account tangle for the group would look like:
data={ purpose=sig, ed25519 pubkey } metadata=...
:arrow_left: this is GROUP_PUBKEYdata=InEnc({ purpose=account, key=ALICE_ACCOUNT_ID }) metadata=... pubkey=GROUP_PUBKEY
data=InEnc({ purpose=account, key=BOB_ACCOUNT_ID }) metadata=... pubkey=GROUP_PUBKEY
data=InEnc({ purpose=account, key=CARLA_ACCOUNT_ID }) metadata=... pubkey=GROUP_PUBKEY
Notice how messages msg1βmsg3 are all signed by GROUP_PUBKEY, so outsiders won't know who actually signed this msg (was it Alice? Was it Bob? Was it Carla?) but they can be reassured that it's a valid msg from this account (the group).
:open_hands: Public information:
:shushing_face: Private information:
Internal feed
Now onto the structure of the feed where actual group content is published. This feed is owned by the GROUP account
data=null metadata={ account=GROUP_ID, domain=hash(DOMAIN, IN_KEY) } pubkey=GROUP_PUBKEY
data=InEnc(innerMsg) metadata={ account=GROUP_ID, domain=hash(DOMAIN, IN_KEY) } pubkey=GROUP_PUBKEY
data=CONTENT, metadata={ account=Alice, domain=DOMAIN } pubkey=Alice_phone
data=InEnc(innerMsg) metadata={ account=GROUP_ID, domain=hash(DOMAIN, IN_KEY) } pubkey=GROUP_PUBKEY
data=CONTENT, metadata={ account=Carla, domain=DOMAIN } pubkey=Carla_pc
Notice how msg1 and msg2 are signed by GROUP_PUBKEY, and an outsider cannot know what's going on inside
innerMsg
, because it is internally encryptedinEnc(innerMsg)
. And then the innerMsg itself contains more private metadata such asinnerMsg.metadata.account
andinnerMsg.metadata.tangles
.:open_hands: Public:
:shushing_face: Private:
Requirements met?
innerMsg
domain
of an internal feed is a hashNew problems
:question: What if the inner msg has tangle information too? (In fact it should, because each group member needs to tangle their msgs together) How to construct the inner tangle, what msg IDs do we use for that? How to validate the inner tangle?
:question: For sympathetic replication, we need to allow outsiders to discover the account tangle ID and some internal feed IDs. How should we do that?
:question: How is group exclusion (i.e. epochs) structured? (see ssb-group-exclusion-spec)