local-first-web / auth Goto Github PK
View Code? Open in Web Editor NEWDecentralized authentication and authorization for team collaboration, using a secure chain of cryptological signatures. (Formerly known as ๐ฎ Taco.)
License: MIT License
Decentralized authentication and authorization for team collaboration, using a secure chain of cryptological signatures. (Formerly known as ๐ฎ Taco.)
License: MIT License
An invitation can fail to validate in several ways. Currently we have:
Need to think about how an application would deal with each of these scenarios. At any rate we should use codes in addition to user-facing messages so the app can respond in a structured way
Suppose Bob is removed from the group, and concurrently he invites Charlie, and Charlie joins the group. Charlie's admission is now invalid, because he was invited while concurrently Bob was being removed.
We currently handle this scenario correctly in that Charlie is no longer a member of the group. However we need to do some cleanup, and we currently don't have a way to do it. From #32 :
Currently if Charlie joins and then his invitation is annulled because of this kind of conflict, everyone else lives happily ever after, as if Charlie had never joined. But Charlie did join and so he has the signature chain, so his client app should be told to purge it (assuming it's well-behaved); and someone needs to rotate any keys he might have seen.
None of this is insoluble, but it does require me to rethink some things specific to this implementation.
Currently when we're calculating the group's state from the chain, any actions that we've invalidated because of concurrency issues are simply discarded, and can't enter into the calculation. Instead, I think I'll need to mark those actions as invalid but still feed them through the reducer, so that e.g. an invalidated JOIN can be treated as a REMOVE.
We don't have a way of alerting the group that certain keys need to be rotated. Currently key rotation only happens as part of a conscious REMOVE action by an admin. In the case where someone is removed not by an admin's action but as a mechanical result of the conflict resolution logic, there's not necessarily an admin around to rotate the keys. So we need a completely new mechanism to add an alert to the group's state that certain keys need to be rotated, and to react appropriately to that alert.
The plan:
Now we're in a position to detect this scenario and surface it in team state.
pendingKeyRotations
element to team state. When the reducer sees an invalid ADMIT as above, it should populate this with the keys Charlie could see, using the same logic as when we manually remove someone. Once these keys are rotated, the reducer should remove them from the list.pendingKeyRotations
, they should perform the key rotation.While we're at it, this business with storing the error in context and then retrieving it and using it as details and storing something else in context.error is pretty gross.
I'm having difficulty using localfirst/auth
within an Electron application (main process) because module names in imports aren't specified by the full path (e.g., .js
or /index.js
for directories). Would you welcome a pull request that adds these?
I agree it's a lot less elegant and pretty to look at than the way it's now, but it seems that that's how other typescript packages (e.g., libp2p) deal with the module resolution problem in Node.js.
What do you say?
Thanks!
I came here looking for a solution that allows access control for Y.Docs. You're library is really impressive and thank you for the dedicated work!
I'm not quite sure if Y.js can be used as a storage within crdx. An example with it in the Readme would certainly help making your library more accessible (and afterwards a link back from Y.js github page).
Currently the bySeniority
sort only looks at ADD_MEMBER
links, which are only used in testing
Change current mutual removal tests to follow the invitation process rather than using Team.addForTesting
The top-level await await sodium.ready
in @localfirst/crypto prevents this library from being bundled into cjs (even when dynamically imported) and thereby prevents it from being used in tools such as nexe, pkg or the newer node sea.
Would convering the crypto functions to async functions be a potential workaround?
I don't think there's currently anything stopping me (at the graph level) from adding a lockbox for someone else. For example, I could make a fake lockbox containing user keys, addressed to someone else's device. I haven't thought through what the consequences of that might be -- it's not immediately clear how I might use that to my advantage. But it's probably not great, so should lock that down.
It's possible that I can currently join a team with an existing user's id and then impersonate them. Need to test this to be sure.
A "softer" attack would be to join a team with an existing user's user name, which is definitely possible, and could cause confusion.
The tricky part here would be to sort out collisions that happen concurrently.
In Auth.Connection we ensure that messages are delivered in the correct order. But if we missed an earlier message, we'll just hang and will never throw an informative error (this was the situation with the automerge-repo AuthProvider before we introduced a queue to store messages we weren't ready for yet)
Multiple admins could simultaneously see that there are pending key rotations. It might make sense to add a random delay before rotating keys, or to come up with another way to ensure that these don't pile up.
This happens in Team.checkForPendingKeyRotations
sync.test.ts
Currently a device can only be admitted by the user whose device it is. This works fine when peers connect directly to each other, but it doesn't work for star-shaped networks where every device connects to a sync server.
The reason it's set up this way is because the user has the user-level secret keys and can store them in a lockbox for the device.
But in a star-shaped topology like XDev uses, where everyone connects to a sync server instead of directly to each other, we need the server to be able to admit devices because a user's two devices will never connect directly.
We also need this to support "paper keys", in a situation where someone loses all their devices or their only device.
So we need another mechanism to get the user-level secret keys to the device.
One solution is to use the invitation code to generate a set of temporary keys. Bob on his laptop creates the invitation code, generates keys from it, and stores the user keys in a lockbox that can be opened by those keys.
Commented-out validator from auth\validate.ts
:
signatureKeyIsCorrect: (...args) => {
const [prevState, link] = args
const action = link.body
const { type } = action
// at root link, team doesn't yet have members
if (type === ROOT) return VALID
const { userId } = link.signed
const author = select.member(prevState, userId)
// TODO: test this case
if (link.signed.key !== author.keys.signature) {
const msg = `Wrong signature key. Link is signed with ${link.signed.key}, but ${userId}'s signature key is ${author.keys.signature}`
return fail(msg, ...args)
}
return VALID
},
In the Connection protocol, we confirm that we haven't been spoofed into joining the wrong team: Once we've joined and gotten the full team history, we look for an invitation matching the secret invitation code we were given.
auth/packages/auth/src/connection/Connection.ts
Lines 605 to 613 in 34c4059
This looks right but there are currently no tests covering this scenario.
Many thanks for this library; it looks quite promising and I am very eager to try it out!
I had a few questions to which I could not find information in the documentation (apologies if it is there):
Many thanks,
Julien Malard
README is not more visible on latest https://github.com/local-first-web/auth
Thanks so much for making such a concise and well-defined library for a problem that many p2p and local-first applications will face. What are you using it for? Or is it a hobby project?
I've been working on a decentralized pattern library called Decentralization, off the shelf with Simply Secure. We're currently working on a set of patterns and an introductory handbook for different ways identity can be done in decentralized applications. I'm focusing particularly on researching sets of pros and cons, and where particular libraries may be well suited for particular use cases (or not). I'm working on all this in the github repository, see disposible identity for an early-stage example.
I'd like to include taco and some of it's ideas, perhaps even creating a pattern for 'trust on first use' ... I'd also love to hear how you imagine this being used in applications and which kinds of applications it would be great for (and what are the pitfalls of this approach, as well)! Do you have any time to give more thoughts on broader applications of taco?
Many thanks for this library; it looks quite promising and I am very eager to try it out!
I had a few questions to which I could not find information in the documentation (apologies if it is there):
Many thanks,
Julien Malard
We'll still open a connection and sync with someone who appears to have been removed from the team, in case they have information that would invalidate their removal (e.g. whoever removed them was concurrently demoted or removed themselves).
However, when we're syncing we don't want to provide them any information until we're sure they're still on the team. I think right now a removed member can sync up and continue to get updates to the signature chain.
Using yarn build
results in the following:
tsc -p tsconfig.build.json
src/connection/Connection.ts(55,9): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/device/create.ts(12,45): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/constants.ts(19,43): error TS2339: Property 'TEAM' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/constants.ts(19,63): error TS2339: Property 'TEAM' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/constants.ts(20,44): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/constants.ts(22,17): error TS2339: Property 'EPHEMERAL' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/constants.ts(23,17): error TS2339: Property 'EPHEMERAL' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/decryptTeamGraph.ts(56,27): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<Hash, EncryptedLink>'.
No index signature with a parameter of type 'string' was found on type 'Record<Hash, EncryptedLink>'.
src/team/decryptTeamGraph.ts(58,7): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<Hash, Link<TeamAction, TeamContext>>'.
No index signature with a parameter of type 'string' was found on type 'Record<Hash, Link<TeamAction, TeamContext>>'.
src/team/decryptTeamGraph.ts(75,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'LinkMap'.
No index signature with a parameter of type 'string' was found on type 'LinkMap'.
src/team/decryptTeamGraph.ts(78,24): error TS7006: Parameter 'hash' implicitly has an 'any' type.
src/team/serialize.ts(62,27): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<Hash, EncryptedLink>'.
No index signature with a parameter of type 'string' was found on type 'Record<Hash, EncryptedLink>'.
src/team/serialize.ts(78,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'LinkMap'.
No index signature with a parameter of type 'string' was found on type 'LinkMap'.
src/team/serialize.ts(81,24): error TS7006: Parameter 'hash' implicitly has an 'any' type.
src/team/Team.ts(188,43): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(199,39): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(323,51): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(365,55): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(393,55): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(650,46): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(706,31): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/Team.ts(718,53): error TS2339: Property 'DEVICE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/transforms/removeMemberRole.ts(25,45): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
src/team/transforms/removeRole.ts(14,54): error TS2339: Property 'ROLE' does not exist on type '{ readonly GRAPH: "GRAPH"; readonly USER: "USER"; }'.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
In servers.test.ts there are a bunch of tests demonstrating what a server can and cannot do, but it seems like those permissions are enforced by a combination of two things: (1) if a server isn't an admin (and it probably shouldn't ever be) then it can't add any links to the graph that are admin-only; and (2) there are checks in the Team
public API that will throw if called by a server. But for example I think a server could add an 'ADD_DEVICE' link to the chain directly (without going through the public API).
As a side note we should probably enforce that a server can't ever be an admin.
The taco-chat demo is broken when I try to run it manually, but not when the Cypress tests run.
main
(42cb906 as of writing)pnpm install; pnpm build
pnpm dev:cy:taco
invitations
suitepnpm dev:taco
What happens: Nothing
What should happen: Bob should join Alice's team, they should both see each other in the list of members, and they should both show a team graph that includes Bob's admission.
Today I tried running the demo and was unable to get anything working. I figured out I needed to install yarn
and then install the dependencies, but was unable to proceed much further. I'm not particularly fluent in yarn
(used to npm
), so apologies if this is a case of me simply holding it wrong.
Here's the output from my machine:
nathan@Nathans-MacBook-Pro auth % node -v
v16.16.0
nathan@Nathans-MacBook-Pro auth % git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
nathan@Nathans-MacBook-Pro auth % git log -n 1
commit 49907d409d39a7b9c24f23f82fed1c3541ee05ed (HEAD -> main, origin/main, origin/HEAD)
Author: HerbCaudill <[email protected]>
Date: Fri Sep 16 15:16:22 2022 +0200
update for latest crdx with encrypted chain
nathan@Nathans-MacBook-Pro auth % yarn install
yarn install v1.22.19
[1/4] ๐ Resolving packages...
[2/4] ๐ Fetching packages...
[3/4] ๐ Linking dependencies...
warning "lerna > @lerna/version > @lerna/github-client > @octokit/rest > @octokit/[email protected]" has unmet peer dependency "@octokit/core@>=3".
warning "workspace-aggregator-ab7c30cb-ba03-43b5-82a1-2042247dd175 > demo > @testing-library/[email protected]" has unmet peer dependency "@testing-library/dom@>=7.21.4".
warning "workspace-aggregator-ab7c30cb-ba03-43b5-82a1-2042247dd175 > demo > @windmill/react-ui > [email protected]" has incorrect peer dependency "react@^16.8.0".
[4/4] ๐จ Building fresh packages...
success Saved lockfile.
โจ Done in 14.43s.
nathan@Nathans-MacBook-Pro auth % yarn dev
yarn run v1.22.19
$ run-p watch dev:demo
$ lerna run watch --stream --parallel
$ yarn --cwd demo && yarn --cwd demo dev
[1/4] ๐ Resolving packages...
โ lerna notice cli v3.22.1
lerna info Executing command in 1 package: "yarn run watch"
success Already up-to-date.
@localfirst/auth: $ npm-watch
$ run-p dev:relay dev:serve
@localfirst/auth: No task specified. Will go through all possible tasks
$ cross-env DEBUG='lf*' DEBUG_COLORS=1 node ./scripts/start-relay-server.js
$ vite --port 3000
@localfirst/auth: [build] [nodemon] 2.0.15
@localfirst/auth: [postbuild] [nodemon] starting `npm run -s postbuild`
@localfirst/auth: [build] [nodemon] starting `npm run -s build`
lf:relay:8080 version 3.6.0 +0ms
๐ โฏ Listening at http://localhost:8080
lf:relay:8080 ready +6ms
@localfirst/auth: [postbuild] tscpaths --project /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/tsconfig.build.json --src /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/src --out /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/dist
@localfirst/auth: [postbuild] Replaced 0 paths in 0 files
@localfirst/auth: [postbuild] [nodemon] clean exit - waiting for changes before restart
vite v2.9.0 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 1055ms.
โ [ERROR] [plugin vite:dep-scan] Failed to resolve entry for package "@localfirst/auth". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "@localfirst/auth". The package may have incorrect main/module/exports specified in its package.json.
../node_modules/vite/dist/node/chunks/dep-8db0e223.js:38291:10:
38291 โ throw new Error(`Failed to resolve entry for package "${id}". ` +
โต ^
at packageEntryFailure (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:38291:11)
at resolvePackageEntry (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:38287:9)
at tryNodeResolve (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:38094:20)
at Context.resolveId (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:37902:28)
at Object.resolveId (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:36470:55)
at async resolve (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:36682:26)
at async /Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:36839:34
at async callback (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:918:28)
at async handleRequest (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:698:30)
This error came from the "onResolve" callback registered here:
../node_modules/vite/dist/node/chunks/dep-8db0e223.js:36829:18:
36829 โ build.onResolve({
โต ~~~~~~~~~
at setup (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:36829:19)
at handlePlugins (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:840:23)
at Object.buildOrServe (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:1134:7)
at /Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:2076:17
at new Promise (<anonymous>)
at Object.build (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:2075:14)
at Object.build (/Users/nathan/Documents/Projects/local-first-web/auth/node_modules/esbuild/lib/main.js:1924:51)
at /Users/nathan/Documents/Projects/local-first-web/auth/node_modules/vite/dist/node/chunks/dep-8db0e223.js:36630:54
at Array.map (<anonymous>)
The plugin "vite:dep-scan" was triggered by this import
src/components/App.tsx:1:22:
1 โ import * as auth from '@localfirst/auth'
... [same error repeats many times]
^C
nathan@Nathans-MacBook-Pro auth % yarn dev:cy
yarn run v1.22.19
$ run-p watch dev:cy:demo
$ yarn --cwd demo && yarn --cwd demo dev:cy
$ lerna run watch --stream --parallel
[1/4] ๐ Resolving packages...
โ lerna notice cli v3.22.1
lerna info Executing command in 1 package: "yarn run watch"
@localfirst/auth: $ npm-watch
success Already up-to-date.
@localfirst/auth: No task specified. Will go through all possible tasks
$ cross-env BROWSER=none CYPRESS_REMOTE_DEBUGGING_PORT=9222 run-p dev test:cy
@localfirst/auth: [postbuild] [nodemon] 2.0.15
@localfirst/auth: [postbuild] [nodemon] starting `npm run -s postbuild`
@localfirst/auth: [build] [nodemon] starting `npm run -s build`
$ run-p dev:relay dev:serve
$ cypress open
@localfirst/auth: [postbuild] tscpaths --project /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/tsconfig.build.json --src /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/src --out /Users/nathan/Documents/Projects/local-first-web/auth/packages/auth/dist
@localfirst/auth: [postbuild] Replaced 0 paths in 0 files
@localfirst/auth: [postbuild] [nodemon] clean exit - waiting for changes before restart
$ cross-env DEBUG='lf*' DEBUG_COLORS=1 node ./scripts/start-relay-server.js
$ vite --port 3000
lf:relay:8080 version 3.6.0 +0ms
๐ โฏ Listening at http://localhost:8080
lf:relay:8080 ready +6ms
vite v2.9.0 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 1021ms.
@localfirst/auth: [build] src/connection/Connection.ts(125,27): error TS2339: Property 'userId' does not exist on type 'UserWithSecrets'.
@localfirst/auth: [build] src/connection/Connection.ts(194,40): error TS2322: Type 'SyncMessage<Action, unknown>' is not assignable to type 'SyncMessage<TeamAction, TeamContext>'.
@localfirst/auth: Type 'Action' is not assignable to type 'TeamAction'.
@localfirst/auth: Type '{ type: string; payload: any; }' is not assignable to type 'TeamAction'.
@localfirst/auth: Type '{ type: string; payload: any; }' is not assignable to type 'RotateKeysAction'.
@localfirst/auth: Types of property 'type' are incompatible.
@localfirst/auth: Type 'string' is not assignable to type '"ROTATE_KEYS"'.
@localfirst/auth: [build] src/connection/Connection.ts(499,9): error TS2554: Expected 3 arguments, but got 4.
@localfirst/auth: [build] src/team/bySeniority.ts(12,44): error TS2769: No overload matches this call.
@localfirst/auth: Overload 1 of 2, '(predicate: (this: void, value: unknown, index: number, obj: unknown[]) => value is unknown, thisArg?: any): unknown', gave the following error.
@localfirst/auth: Argument of type '(link: TeamLink) => boolean' is not assignable to parameter of type '(this: void, value: unknown, index: number, obj: unknown[]) => value is unknown'.
@localfirst/auth: Types of parameters 'link' and 'value' are incompatible.
@localfirst/auth: Type 'unknown' is not assignable to type 'TeamLink'.
@localfirst/auth: Type '{}' is missing the following properties from type 'Link<TeamAction, TeamContext>': hash, body, signed
@localfirst/auth: Overload 2 of 2, '(predicate: (value: unknown, index: number, obj: unknown[]) => unknown, thisArg?: any): unknown', gave the following error.
@localfirst/auth: Argument of type '(link: TeamLink) => boolean' is not assignable to parameter of type '(value: unknown, index: number, obj: unknown[]) => unknown'.
@localfirst/auth: Types of parameters 'link' and 'value' are incompatible.
@localfirst/auth: Type 'unknown' is not assignable to type 'TeamLink'.
@localfirst/auth: Type 'unknown' is not assignable to type 'Link<TeamAction, TeamContext>'.
@localfirst/auth: [build] src/team/bySeniority.ts(17,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'Link<Action, unknown>'.
@localfirst/auth: Type '{}' is missing the following properties from type 'Link<Action, unknown>': hash, body, signed
@localfirst/auth: src/team/getMissingLinks.ts(1,18): error TS2305: Module '"crdx"' has no exported member 'HashGraph'.
@localfirst/auth: src/team/getMissingLinks.ts(5,27): error TS2339: Property 'body' does not exist on type 'unknown'.
@localfirst/auth: [build] src/team/membershipResolver.ts(144,57): error TS2551: Property 'userId' does not exist on type 'LinkBody<TeamAction, TeamContext>'. Did you mean 'user'?
@localfirst/auth: Property 'userId' does not exist on type '{ user: User; timestamp: number; prev: string[]; } & RootAction & TeamContext'.
@localfirst/auth: [build] src/team/redactUser.ts(5,3): error TS2741: Property 'userId' is missing in type '{ roles: undefined[]; userName: string; keys: Keyset; }' but required in type 'Member'.
@localfirst/auth: [build] src/team/Team.ts(108,9): error TS2345: Argument of type '{ user: UserWithSecrets; reducer: Reducer<TeamState, TeamAction, TeamContext>; resolver: Resolver<TeamAction, TeamContext>; initialState: TeamState; rootPayload: { ...; }; graphKeys: KeysetWithSecrets; }' is not assignable to parameter of type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: Object literal may only specify known properties, and 'graphKeys' does not exist in type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: src/team/Team.ts(121,9): error TS2345: Argument of type '{ user: UserWithSecrets; reducer: Reducer<TeamState, TeamAction, TeamContext>; resolver: Resolver<TeamAction, TeamContext>; initialState: TeamState; graph: any; graphKeys: KeysetWithSecrets; }' is not assignable to parameter of type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: Object literal may only specify known properties, and 'graph' does not exist in type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: [build] src/team/Team.ts(152,23): error TS2339: Property 'getGraph' does not exist on type 'Store<TeamState, TeamAction, {}>'.
@localfirst/auth: src/team/Team.ts(625,7): error TS2345: Argument of type '{ user: UserWithSecrets; reducer: Reducer<TeamState, TeamAction, TeamContext>; resolver: Resolver<TeamAction, TeamContext>; initialState: TeamState; graph: any; graphKeys: KeysetWithSecrets; }' is not assignable to parameter of type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: Object literal may only specify known properties, and 'graph' does not exist in type 'StoreOptions<TeamState, TeamAction, TeamContext>'.
@localfirst/auth: [build] src/team/Team.ts(740,53): error TS2339: Property 'userId' does not exist on type 'UserWithSecrets'.
@localfirst/auth: src/team/Team.ts(745,53): error TS2339: Property 'userId' does not exist on type 'UserWithSecrets'.
@localfirst/auth: src/team/Team.ts(800,52): error TS2554: Expected 1 arguments, but got 2.
@localfirst/auth: [build] src/team/types.ts(8,3): error TS2305: Module '"crdx"' has no exported member 'HashGraph'.
@localfirst/auth: src/team/validate.ts(30,19): error TS2339: Property 'userId' does not exist on type 'LinkBody<TeamAction, TeamContext>'.
@localfirst/auth: [build] src/team/validate.ts(65,32): error TS2551: Property 'userId' does not exist on type '{ user: User; timestamp: number; prev: string[]; } & ChangeMemberKeysAction & TeamContext'. Did you mean 'user'?
@localfirst/auth: src/util/testing/Network.ts(103,7): error TS2554: Expected 3 arguments, but got 4.
@localfirst/auth: [build] src/util/testing/setup.ts(42,41): error TS2554: Expected 1-2 arguments, but got 3.
@localfirst/auth: [build] [nodemon] app crashed - waiting for file changes before starting...
In crdx Store.dispatch, the you don't have to provide a graph key every time; if no key is provided, we use the same key that was used for the previous link. If the link has multiple parents, we choose the first one. This might be a source of undefined behavior though.
This thing should have some functionality beyond managing users ๐ค
In the bySeniority
sort function, we just check if a
is a predecessor of b
and if not, we assume that b
is a predecessor of a
. But they could have been added concurrently, in which case I'm not sure the current behavior is deterministic.
In certain situations we need to know the dependency structure of a graph in order to decrypt it properly. We currently solve that by passing around a ParentMap or ChildMap or whatever. It would be simpler to just embed that information with each link.
...from the dist folder in the most recent release
This is from a comment in membershipResolver.test.ts:
sometimes (50% of the time?) when we eliminate duplicate ADD_MEMBERs,we're eliminating one that would have needed to have come BEFORE something else, in this case the CHANGE_MEMBER_KEYs action that happens after that person is admitted. Here's an example of a bad graph that you can end up with that way:
๐ฉ๐พ ROOT
ADD_MEMBER:๐จโ๐ฆฒ
<== ADD_MEMBER:๐ณ๐ฝโโ๏ธ was removed from here
INVITE:kPFx4gwGpuWplwa
INVITE:tiKXBLLdMbDndJE
ADMIT:๐ณ๐ฝโโ๏ธ
CHANGE_MEMBER_KEYS:{"type":"MEMBER","name":"๐ณ๐ฝโโ๏ธ",...} <== we can't do this because ๐ณ๐ฝโโ๏ธ hasn't been added yet
ADD_DEVICE:๐ณ๐ฝโโ๏ธ:laptop
ADD_MEMBER:๐ณ๐ฝโโ๏ธ
INVITE:dQRE52A+7UGr8X9
ADD_MEMBER:๐ด
INVITE:j6cC8ZyjyhuojZw
ADMIT:๐ด
CHANGE_MEMBER_KEYS:{"type":"MEMBER","name":"๐ด",...}
ADD_DEVICE:๐ด:laptop
Here's how that graph should have been resolved:
๐ฉ๐พ ROOT
ADD_MEMBER:๐จโ๐ฆฒ
ADD_MEMBER:๐ณ๐ฝโโ๏ธ <== in the bad graph,this ADD_MEMBER was discarded as a duplicate
INVITE:fNpSg0uBcW1vYvf
ADD_MEMBER:๐ด
INVITE:PkD7SISvUt/3YlJ
ADMIT:๐ณ๐ฝโโ๏ธ
CHANGE_MEMBER_KEYS:{"type":"MEMBER","name":"๐ณ๐ฝโโ๏ธ",...}
ADD_DEVICE:๐ณ๐ฝโโ๏ธ:laptop
<== ADD_MEMBER:๐ณ๐ฝโโ๏ธ was removed from here
INVITE:Pu6NaY6HfbITAf6
INVITE:7vVS0NXz+u15Mx2
ADMIT:๐ด
CHANGE_MEMBER_KEYS:{"type":"MEMBER","name":"๐ด",...}
ADD_DEVICE:๐ด:laptop
If we wanted to restrict a sync server's read access to application data (as opposed to the team membership data), we would currently have to create a new role that is only for human members (and ensure that every member was added to it), and encrypt the application data with that role's keys. It might make more sense to separate out the graph keys (for encrypting the team graph) from the team keys (for encrypting data for human members of the team).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.