storacha / project-tracking

🐾 Used as central/default repo for project management, backlog, etc.
0 stars 0 forks source link

Spaces are always available in the Console #124

Open Peeja opened 3 weeks ago

Peeja commented 3 weeks ago

Acceptance Criteria

  1. When I create a new Space using the JS API, it should be visible on the Console without having to manually add recovery.

Notes

Follow the steps in the docs, except for adding the recovery at the end. Essentially, as of this issue, the client should do that last step automatically.

Peeja commented 2 weeks ago

This is odd. My impression was that a space with no "recovery" was not associated with the account (the mailto:), only the agent which created it. Yet somehow, the Console is aware of the spaces, it just can't display them. It knows about them because they're listed in the response to subscription/list. Why is that? I don't even see that capability documented in the spec.

The invocation returns results like:

        {
            "provider": "did:web:web3.storage",
            "consumers": [
                "did:key:z6MkeVvvDxa7yVva9tWe1MyPzP7H31NeYfSgosJ3o929ggpF"
            ],
            "subscription": "bafyreigqg6sm7dloai7nsn7sm6riybyd4ggh3ig5vd3r63nhso4ojasmv4"
        },

What is that value for subscription? It looks like a CID, but I'm not sure how to dereference it. In the tests it looks like it should be three DIDs smushed together; is that code automatically dereferencing the link?

I still can't find where the names for the spaces I do see come from, which is even stranger to me. 😕

One more question: When creating a recovery, why is the "agent" the Space's key with the Account's DID? That reads super strangely to me.

hannahhoward commented 1 week ago

To answer your question, I want to run through a conceptual overview of how spaces are supposed to work.

A space is just a namespace for files identified by a public key DID. The authority over that space is initially the private key of the DID.

When you create a space, a key pair is created in whatever agent (i.e. console, w3-cli, or the js-lib) you are running. Then absolute authority is delegated to the agent, and private key of the space is actually discarded. At this point, authority for the space is with the agent.

There are now two SEPERATE categories of things I can do to expand what I can do with the space:

  1. Delegating access to the space

My agent is now fully in control of the space. Without without any involvement of web3.storage, I could share access to this space with another agent by producing and sharing a UCAN from my agent to the other agent delegating capabilities for the space.

However, this would be super hard to manage for your average user.

So we have another mechanism for sharing access. This is the account. And account is just a DID that's not did:key. I can make a UCAN delegation that goes from my agent did:key to the did:mailto for the account.

That's what createRecovery() is actually doing -- it's creating a UCAN that delegates access from the agent DID to the account DID (i.e. the mailto)

Then

await client.capability.access.delegate({
  space: space.did(),
  delegations: [recovery],
})

Is simply invoking access/delegate that sends this UCAN delegation over the wire for this space.

Now, on the other side, a new agent comes along who wants to get access to the spaces for the account

they invoke access/request to claim access to all capabilities associated with an account. This starts the email authorization flow and triggers web3.storage to send an email to the associated email address authorizing the action.

then they can call access/claim to receive delegations for any spaces previously delegated to the account. these delegations will be a chain: space did -> original agent did original agent did -> account did (when you call create recovery and access/delegate) account did -> new agent did

Importantly, because the account did is a did:mailto it's not signed with a private key. Instead it's signed with an attestation from web3.storage that they've performed the email authorization flow.

The delegations received with access/claim are what shows up in your space list. (and after you do access/claim they're written to local storage, which means they don't get updated. Presumably, to make this autoupdate, we should call access/claim again, on page load, even if there are spaces in local storage, and then update the list when access/claim returns)

  1. Add a provider for the space

All of the above have to do with delegating the ability to perform capabilities on a space. However, for capabilities to be invoked, you need a provider for those capabilities. So a space must be provisioned with a provider to do almost anything with it. When a space has a provider, storage commands (i.e. blob/add etc) invoked on a space routed to a provider to actually care them out. In this case, the provider is web3.storage/Storacha. The code in the JS lib tutorial is that connects the space to the provider:

await myAccount.provision(space.did())

Under the hood it calls provider/add -- connecting the space to the provider. This connection then will show up with subscription/list -- btw, these capabilities are documented in specs, in the provider spec, but they're almost entirely out of alignment with what they do, hence the reversion PR. There are a lot of reasons the proposed revision makes better sense -- it's a bit weird to have /provider/add create a subscription under the hood, and not a relationship between a provider and a count, but at least now the spec matches the implementation.

While web3.storage is the only provider, anyone could build a different provider by meeting the capabilities. And theoretically a provider could just be a local disk.

Obviously the most confusing thing here is that it says myaccount.provision -- I'd expect this to be giving access to a space to an account, but it's not (this is also the command we propose to change to actually create such access by default). What your actually saying here is "this account has previously purchased the right to invoke capabilities with a provider, I'd now like to route invocations for this space through this provider". But if I want to actually delegate access to this space to the account, that comes only through the recovery for now.


All of this is a long way of saying: And agent granted access to an account through a provider can call subscription/list to get a list of all the spaces that are using this provider to invoke capabilities. BUT unless the original agent that created the space delegated access to that space to account, the new agent won't be able to claim access to the space through access/claim

hannahhoward commented 1 week ago

You might also say: why so complicated?

Well, there are certainly cases where different agents shouldn't have access to all spaces in an account. One form or another of this is what happens with large applications that have one account with us but let their users upload to a single space in their account.

In this case, the ucans are transmitted directly to the client for certain spaces, without access/claim

I don't exactly know why you wouldn't want those still to have a recovery for someone with full access to the account, so again, I think the default flow should automatically create a recovery.

hannahhoward commented 1 week ago

@alanshaw I'd love your comments on whether all the above is correct.

Peeja commented 1 week ago

Excellent. That's all super helpful and fits with my understanding. But I have some more questions:

  1. What is the purpose of a Space at all? It's supposed to be short for "namespace", but with in a Space, everything is identified by CID anyhow. If they don't have names, how can it be a namespace?
  2. Why do we need to delegate anything to Storacha? We're already POSTing all of these UCANs to Storacha. Aren't we already trusting Storacha implicitly, as a central service, to handle these requests? The whole web3 part seems like a complicated charade on top of that.
  3. Under UCAN, who has the original * authority over a resource? Is it always the resource itself? Is that why the Space is able to delegate to the Agent?
  4. What does it mean to have the capability to store data in the Space? Who actually cares? Is it not actually the capability to enumerate the contents of the Space that matters?
  5. The association between the Account (did:mailto:) and the later Agents (did:key:) is proven by attestation signed by Storacha. Who is checking this attestation, and why do they trust the central Storacha Principal? Could another Principal in the network serve this function as well?
hannahhoward commented 1 week ago
  1. I think the namespace here is about the grouping underneath a space, not the names inside, but yes, named items would be good.
  2. We don't delegate to Storacha. Storacha is the provider, not the account. The account is just an email. We delegate to the account. Yes, you put trust in a provider. There is always some trust. But you can choose a different provider. (and FWIW, the account delegation would remain valid regardless of the provider)
  3. I mean for UCAN in general, it's just whatever the last root is in the proofs, and then it's up to the person processing it to decided on whether the last root ultimately has authority over the resource. But in our case for spaces, it's the space did:key.
  4. Having the capability to store data in the space means if my space has a provider, I am then able to use the provider to store data. That seems useful.
  5. Who is checking the attestation? I mean Storacha itself is for one when it later receives an invocation. Presumably someone else can serve this role. As for trusting the principal, yes, attestations are a trust relationship in most cases. You can verify that the attestation was performed by a given entity, and you either trust that entity or don't.
hannahhoward commented 1 week ago

One piece I think is perhaps confusing providers sell plans to an account. But an account is just an email. One account can have as many providers as it likes. did:mailto:hannah@somedomain.com is an account, whether or not it's connected to a plan on a provider.

Peeja commented 1 week ago
  1. We do delegate to Storacha: you ultimately delegate the capability to add to a space to the Provider. Right?
  2. Okay. So each ability separately defines what Principal(s) have inherent authority to do something, who thereby are able to delegate that authority? That has to be part of the definition in each ability's spec?

I think most of my concern comes down to a confusion or conflation between several roles that Storacha (currently) plays:

Those roles are currently all served by the same service, but (I believe) don't need to be, and I have a sense we want to divide the third role up a bit further. If these aren't separate roles, all of this UCAN business seems incredibly overwrought: we're specifically trying to decentralize this system. If they are separate roles, then we have a surprising amount of trust between the roles, or I'm missing something.

Peeja commented 1 week ago

Hoooooold up:

6.3 Proof Chaining

Each capability MUST either be originated by the issuer (root capability, or "parenthood") or have one-or-more proofs in the prf field to attest that this issuer is authorized to use that capability ("introduction"). In the introduction case, this check MUST be recursively applied to its proofs until a root proof is found (i.e. issued by the resource owner).

Does someone delegate authority to Storacha to attest the delegation from the Account to the Agent?

Peeja commented 1 week ago

Alright, I think @fforbeck and I finally have our collective heads around all this. @fforbeck is taking lead for now, and we'll sync up a bit later.

fforbeck commented 6 days ago

Changes

The createSpace function in client.js has been enhanced to streamline the space creation process. The new workflow now automatically handles the following steps:

This update removes the need for manual steps, ensuring the recovery account is set up as part of the space creation process.

PRs: