-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assumptions/questions after a few days of reading #26
Comments
Hello @timsim00 ! That's an amazing summary and list of questions, thank you again for putting it together. Even though this library is already a few years old, it's only now becoming a viable platform for apps. The truth is that we're still figuring out a set of generic primitives to base p2p, Byzantine fault tolerant apps on. I think we've got all the pieces by now, but as you hinted I feel we need a set of established patterns / recipes to make it easier. Data modelYeah, class ChatGroup extends HashedObject {
moderators: MutableSet<Identity>;
messages: MutableSet<Message>;
...
} So the Like you said, internally a So the data model can be seen as transforming mutable state into an append-only graph that can be streamed to other peers. SpacesIn general, a Space defines the sharing boundary an app will use. In the chat example, it makes sense to use the ChatGroup class as our sharing boundary: folks can join a chat group (and hence get access to all its contents). In document-based apps, a document is a natural candidate for a Space. Usually, the methods
As you mention, it is possible to use spaces as an inter-app integration method. For example, if you define types for something like Slack or Discord, you could have a space for your entire workspace, with all the threads, but you could also define a space per-channel, to make this channels available in other apps / contexts besides your workspace (for example, you may want to make a channel public and embed it into your website, or bridge a channel between two different workspaces to help to organizations collaborate). But this is rather advanced and I've never used spaces like that yet! Access controlThis data model makes access control especially challenging, because the graph we're using to represent data may be replicated un-evenly to peers. So folks may use their access rights concurrently with they being granted or revoked, and some peers may see their actions as valid or invalid initially, but may have to change their mind as more data is replicated. That's what the Here's an example, again from chat groups, about the rule for deleting messages: protected createDeleteAuthorizer(msg: Message, author: Identity): Authorizer {
// any member may delete their own messages, but only moderators can delete other's
const membershipReq = author.equals(msg.getAuthor()) ?
this.getMembers()
:
this.getAdmins();
const membershipAuth = membershipReq.createMembershipAuthorizer(author);
return Authorization.chain(super.createDeleteAuthorizer(msg, author), membershipAuth);
} That's saying that we will require a membership attestation (that's an object that serves as proof that something belonged to a given set at the time of this change), and if the author of the message is the same as the author doing the change, we will require an attestation of the author belonging to the members set of the chat group - but if the author of the change (the deletion) is not the same as the author of the message, then he needs to attest that they belong to the moderators set. This API is not intuitive enough yet, we'll try to find better primitives for expressing this. But in this terms, upon adding an Peer DiscoveryThe default behavior when you use the type PeerGroupInfo = {
id: string, // a string identifying this peer group
localPeer: PeerInfo, // our local identity
peerSource: PeerSource // a method that will give us random peers to connect
}; peerSource may pull peers from an external source, like a database, or use the local replica of an HHS object (like the As things stabilize a bit more, we'll write a reference manual trying to iron out how to go about all this things. I'll go over your list of questions as soon as I got a bit more time, please just add more questions if the above is not clear. If perchance you'd be interested in working with us to get the library in shape for app creation, or want to suggest features you feel are missing, or how to go about documenting all this stuff, all would be very welcome. Thank you again for your analysis & questions, I feel this will help us already! |
Hello @sbazerque, Talking through the playground logic:Any help confirming this would be appreciated. a. Creator/first peer (peer1) (setup id/store/resources)a1. create the type of store I want (default is memory), but we want Idb. b. Pulling that foundation into an app:b1. create an instance of the app's data model, DocSpace, which is an immutable HashedObject (id, author, resources) c. Second Peer (peer2)(DocSpace already exists somewhere, we have the ID for it) Majors: Resources, HashedObject, Space, Store, app's data model DocSpace
Identity: how to add register/login/logout functionality?
Data Model / ACLStarting to understand, but still not exactly sure how to model the orgSpace example above, or do ACL. Looks like the default mode for a space is everyone can read/write a mutable within a space. But ACL is implemented via CausalSet and membership, as in your createDeleteAuthorizer example above. I need to play around with that. Saving
|
Hello @timsim00, thanks again for your insightful questions!
Right now, the store is serving as a sort of identity store as well. If you want to save objects signed by a given identity, both the
This looks good to me, it makes sense to use both
Also looks good to me. The actual API for broadcasting / lookup supports either full or partial hashes (the 3 words encode 36 bits of the hash). But other forms of encoding either the full or a part of the hash could be used (e.g. QR codes).
I agree.
I'll try to frame this, I may be getting what you want wrong, but I'd say: To login in a new device, you need to trust the device, since then it will be acting upon shared data on your behalf.
You need both the
Yes, if a new account is going to be used, you can just create a new
The way I view it, the private key should be securely moved to the device, and effectively erased to log out.
OK I think maybe what you are aiming for is supporting several users on the same device, even if they don't trust each other? At this point I think you'd have to rely on the operating system's user system to provide real protection of each users data/keys from each other. I don't think we can use the browser for that ATM, since HHS doesn't use encryption at rest. You could always use a list of username / pwd to provide user switching, but the underlying data would still be accessible by all users if they poke in IndexedDB. More like a user-switching solution, without real protection from one another in the same browser. Maybe what could be done to support something like user login on-device would be to have a new store, that wraps the IndexedDb but adds encryption-at-rest using a passphrase. It wouldn't be complete protections, since something may be inferred from the structure of the encrypted entries inside IndexedDb, but it would be pretty good still. What do you think?
It's really inside each store, and by doing it you enable the store to save objects signed by that identity, in practice enabling that identity to write to the store.
Yes, the keypair is used automatically whenever an object authored by that identity is saved.
Good catch! The signing is done when you save the object in the store, and the signature verification is done right after receiving new objects during sync.
Yes, static (immutable) permissions can be modeled passing a
The purpose of
It would be the same, but the
You may be right. What's in there is just all the environment that the object needs to work. Open to suggestions!
Yes, sorry for the confusion. The example is meant to show how to save stuff to the store and retrieve it using its hash, but you're right, it wouldn't be syncrhonized. |
Thanks for the feedback, much appreciated! I will continue to digest and experiment. The big thing for me right now is getting my understanding up to a level so I can model the data, do auth and basic CRUD.
Yes, good catch. I can't rely on users having their own account on a device....ie. two different users login from the Guest account on a friends' device. Just need to cover my bases. I can always encrypt data if it needs to be protected; most likely just an edge case atm. At the very least it sounds like I can do user switching and HHS can handle that. |
I'm really excited to be learning HHS, as I can see a lot of great, very intelligent, work has gone into it already. From reading the documentation it sounds like it will work for the app I'm building. Now I'm trying to understand how I would use HHS to implement the data model for my app. I've used many different databases but nothing like p2p. I'm very new to this space (no depth of knowledge of Merkle-DAG's, spanning trees, WebRTC etc...but I get graphs). I'm trying to bridge the gap between what I want to do and how HHC works. Here's what I've been able to piece together after a few days with a fresh set of eyes. My apologies in advance if I missed anything obvious in the docs. If anyone is able to confirm or correct my understanding and point me in the right direction I would really appreciate it! Open source examples are always welcome.
High Level
I can put my static SPA app on a CDN and peers will sync app data among themselves. A signaling server is needed to aid in this process, but load on the server is not high. HHS is like a toolbox with tools to enable this.
A Store is an abstract for actual data persistence to a peer, for instance IndexedDB in the browser. An app's data model is made up of many interlinked spaces. Sync logic takes data in a 'Space' and updates the store on a peer using the verify function of each class. The full graph of spaces is represented across the network of peers. Individual peers only have the spaces they edited or requested. Access is granted or revoked using the CausalSet. The whitepaper says CapabilitySet but I think the API has changed since it was written.
Spaces
Spaces can be nested within other spaces by referencing those spaces. Spaces can be discovered independently (of any particular app) via a 3-word code. A space is an abstract for exactly one grouping of data, "a chat room, an article, a blog, an ecommerce store, etc." A class (written by a dev) defines that grouping of data, a potential combination of literals and references to other spaces/class instances, based on business logic. A space is implemented when a class inherits from either an immutable HashedObject or a mutable abstract, ie MutableSet which is a MutableObject.
When to use one or the other, I'm not sure. Seems like a good use-case for HashedObject would be an error log. Or would an error log be represented as a HashedObject nested inside a MutableSet? What are some good use-cases for an immutable space? Will most app data be mutable?
"They can be universally looked up using 3-word codes, like suburb-suburb-awake." If a space really is a class instance, does that mean every single class instance needs a 3-word code? Is this what the Shuffle class is for? If I code a BlogSet class and an Article class, and there are 3 articles in the blog, does that mean there must be 4 spaces total for the blog? Is there ever a situation where the blog would be just one space?
Data Model
(my main question...how to model my app data?)
It seems like an application data model would represent a graph, with the application (for a particular user) pointing to some logical root. Various spaces/classes would then be organized under the root in a way that makes sense according to the business logic. Each user would have various access to all or parts of this node. ie.
App Data (for Acme Employee: Bob) =>
Acme Corp {
Meta: {orgSpace: "acme-corp-best"}
EmployeeSet -> Employee: {name, startDate, title, boss...}
CustomerSet -> Customer: {name, address...}
OrderSet -> Order: {date, total, Lines -> LineItem: {product, qty, price, amount, total}, customer...}
ProductSet -> Product: {name, priceSet...}
OutsideMeetingSet -> Meeting: {mtgDateTime, discussion, Invites -> Invite:{orgSpace: "abc-corp-great"}}
}
*Bob may be granted access to all or parts of this graph depending on his role at Acme.
*This is not the app I want to build, just an example.
Peer Discovery
"Peers in the network form application-defined groups over which mutable objects are synchronized. The method for obtaining peers is also application defined..." I'm not sure what this means. Let's say both Acme Corp ("acme-corp-best") and ABC Corp ("abc-corp-great") are pointing to their respective Org graphs. Does HHS have logic, or does the app need custom logic, to make sure Acme employees are primarily trying to sync with other Acme employees and not ABC employees, other than for shared data like an OutsideMeeting discussion Forum which both companies can access? How does an app define peer groups? Implicitly? Or is there example code for this?
Other questions:
Again, great work on HHC and thank you to anyone who can help confirm/deny assumptions and answer questions. It looks very promising!
The text was updated successfully, but these errors were encountered: