Skip to content
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

Feat/bump0.9 #116

Merged
merged 3 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/access-api/src/service/voucher-redeem.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function voucherRedeemProvider(ctx) {
})

ctx.config.METRICS.writeDataPoint({
blobs: [ctx.config.ENV, 'new_account'],
blobs: [ctx.config.ENV, 'new_account_v1'],
doubles: [1],
})

Expand Down
15 changes: 7 additions & 8 deletions packages/access-api/test/helpers/context.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Signer } from '@ucanto/principal/ed25519'
import { buildConnection } from '@web3-storage/access'
import { connection } from '@web3-storage/access'
import anyTest from 'ava'
import dotenv from 'dotenv'
import { Miniflare } from 'miniflare'
Expand Down Expand Up @@ -38,15 +38,14 @@ export async function context() {
modules: true,
bindings,
})
const { connection } = await buildConnection(
principal,
// @ts-ignore
mf.dispatchFetch.bind(mf),
new URL('http://localhost:8787')
)
return {
mf,
conn: connection,
conn: await connection(
principal,
// @ts-ignore
mf.dispatchFetch.bind(mf),
new URL('http://localhost:8787')
),
service: Signer.parse(bindings.PRIVATE_KEY),
issuer: principal,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/access-api/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ unsafe = { bindings = [
# Staging
[env.staging]
name = "w3access-staging"
routes = [{ pattern = "access-api-staging.web3.storage", custom_domain = true }]
workers_dev = true
vars = { ENV = "staging", DEBUG = "false" }
build = { command = "scripts/cli.js build --env staging", watch_dir = "src" }
kv_namespaces = [
Expand All @@ -76,7 +76,7 @@ unsafe = { bindings = [
# Production
[env.production]
name = "w3access"
routes = [{ pattern = "access-api.web3.storage", custom_domain = true }]
routes = [{ pattern = "access.web3.storage", custom_domain = true }]
vars = { ENV = "production", DEBUG = "false" }
build = { command = "scripts/cli.js build --env production", watch_dir = "src" }
kv_namespaces = [
Expand Down
87 changes: 42 additions & 45 deletions packages/access/src/agent.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import * as DID from '@ipld/dag-ucan/did'
import * as Client from '@ucanto/client'
import { delegate } from '@ucanto/core'
// @ts-ignore
// eslint-disable-next-line no-unused-vars
import * as Ucanto from '@ucanto/interface'
import * as DID from '@ipld/dag-ucan/did'
import { Peer } from './awake/peer.js'
import * as Client from '@ucanto/client'
import * as CAR from '@ucanto/transport/car'
import * as CBOR from '@ucanto/transport/cbor'
import * as HTTP from '@ucanto/transport/http'
import { delegate } from '@ucanto/core'
import * as Voucher from './capabilities/voucher.js'
import { URI } from '@ucanto/validator'
import { Peer } from './awake/peer.js'
import * as Account from './capabilities/account.js'
import { Websocket } from './utils/ws.js'
import * as Voucher from './capabilities/voucher.js'
import { stringToDelegation } from './encoding.js'
import { URI } from '@ucanto/validator'
import { Websocket } from './utils/ws.js'

/**
* @template T
* @typedef {{
* store: import('./stores/types').Store<T>
* connection: Ucanto.ConnectionView<import('./types').Service>,
* url?: URL,
* fetch?: typeof fetch
* service: Ucanto.Principal
* fetch: typeof fetch
* data: import('./stores/types').StoreData<T>
* }} AgentOptions
*/
Expand All @@ -35,20 +34,16 @@ import { URI } from '@ucanto/validator'
* }} AgentCreateOptions
*/

const HOST = 'https://access-api.web3.storage'
const HOST = 'https://access.web3.storage'

/**
* @template {string} T
* @param {Ucanto.Principal<T>} principal
* @param {typeof fetch} _fetch
* @param {URL} url
* @returns { Promise<{service: Ucanto.UCAN.PrincipalView, connection: import('@ucanto/interface').ConnectionView<import('./types').Service>}>}
* @returns { Promise<import('@ucanto/interface').ConnectionView<import('./types').Service>>}
*/
export async function buildConnection(principal, _fetch, url) {
const rsp = await _fetch(url + 'version')
const { did } = await rsp.json()
const service = DID.parse(did)

export async function connection(principal, _fetch, url) {
const connection = Client.connect({
id: principal,
encoder: CAR,
Expand All @@ -60,36 +55,33 @@ export async function buildConnection(principal, _fetch, url) {
}),
})

return { service, connection }
return connection
}

/**
* @template {Ucanto.Signer} T
* Agent
*/
export class Agent {
/** @type {Ucanto.Principal|undefined} */
#service

/** @type {typeof fetch} */
#fetch

/**
* @param {AgentOptions<T>} opts
*/
constructor(opts) {
this.store = opts.store
this.service = opts.service
this.url = opts.url || new URL(HOST)
this.fetch = opts.fetch
this.connection = opts.connection
this.data = opts.data
this.issuer = opts.data.principal
this.store = opts.store
this.data = opts.data

// validate fetch implementation
if (!this.fetch) {
if (typeof globalThis.fetch !== 'undefined') {
this.fetch = globalThis.fetch.bind(globalThis)
} else {
throw new TypeError(
`Agent got undefined \`fetch\`. Try passing in a \`fetch\` implementation explicitly.`
)
}
}
// private
this.#fetch = opts.fetch
this.#service = undefined
}

/**
Expand All @@ -112,21 +104,25 @@ export class Agent {
}

const data = await opts.store.load()
const { connection, service } = await buildConnection(
data.principal,
_fetch,
url
)
return new Agent({
connection,
service,
connection: await connection(data.principal, _fetch, url),
fetch: _fetch,
url,
store: opts.store,
data,
})
}

async service() {
if (this.#service) {
return this.#service
}
const rsp = await this.#fetch(this.url + 'version')
const { did } = await rsp.json()
this.#service = DID.parse(did)
return this.#service
}

did() {
return this.data.principal.did()
}
Expand All @@ -136,6 +132,7 @@ export class Agent {
*/
async createAccount(email) {
const account = await this.store.createAccount()
const service = await this.service()
const accDelegation = await delegate({
// @ts-ignore
issuer: account,
Expand All @@ -156,12 +153,12 @@ export class Agent {
const inv = await Voucher.claim
.invoke({
issuer: this.data.principal,
audience: this.service,
audience: service,
with: account.did(),
nb: {
identity: URI.from(`mailto:${email}`),
product: 'product:free',
service: this.service.did(),
service: service.did(),
},
proofs: [accDelegation],
})
Expand All @@ -171,13 +168,13 @@ export class Agent {
throw new Error('Account creation failed', { cause: inv.error })
}

const voucherRedeem = await this._waitForVoucherRedeem()
const voucherRedeem = await this.#waitForVoucherRedeem()

const accInv = await Voucher.redeem
.invoke({
issuer: this.data.principal,
audience: this.service,
with: this.service.did(),
audience: service,
with: service.did(),
nb: {
account: account.did(),
identity: voucherRedeem.capabilities[0].nb.identity,
Expand All @@ -196,7 +193,7 @@ export class Agent {
this.store.save(this.data)
}

async _waitForVoucherRedeem() {
async #waitForVoucherRedeem() {
const ws = new Websocket(this.url, 'validate-ws')
await ws.open()
ws.send({
Expand Down Expand Up @@ -267,7 +264,7 @@ export class Agent {
const inv = await Account.info
.invoke({
issuer: this.issuer,
audience: this.service,
audience: await this.service(),
with: account,
proofs,
})
Expand Down
6 changes: 2 additions & 4 deletions packages/access/src/awake/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class Peer {

// step3 - awake/res send
const ucan = await UCAN.issue({
issuer: this.agent.data.principal,
issuer: this.agent.issuer,
audience: this.nextdid,
capabilities: [{ with: 'awake:', can: '*' }],
facts: [
Expand Down Expand Up @@ -188,9 +188,7 @@ export class Peer {

// Pin signature
const bytes = u8.fromString(this.nextdid.did() + this.pin.toString())
const signed = await this.agent.data.principal.sign(
await sha256.encode(bytes)
)
const signed = await this.agent.issuer.sign(await sha256.encode(bytes))
this.channel.sendMsg(this.nextdid, {
did: this.did,
sig: u8.toString(signed, 'base64'),
Expand Down
4 changes: 2 additions & 2 deletions packages/access/src/cli/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Verifier } from '@ucanto/principal/ed25519'

/** @type {Record<string,string>} */
const envs = {
production: 'https://access-api-staging.web3.storage',
staging: 'https://access-api.web3.storage',
production: 'https://access.web3.storage',
staging: 'https://w3access-staging.protocol-labs.workers.dev',
dev: 'https://w3access-dev.protocol-labs.workers.dev',
local: 'http://127.0.0.1:8787',
}
Expand Down
2 changes: 2 additions & 0 deletions packages/access/test/awake.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ describe('awake', function () {
const agent1 = await Agent.create({
store: await StoreMemory.create(),
fetch: globalThis.fetch || fetch,
url: new URL('http://127.0.0.1:8787'),
})
const agent2 = await Agent.create({
store: await StoreMemory.create(),
fetch: globalThis.fetch || fetch,
url: new URL('http://127.0.0.1:8787'),
})
const responder = agent1.peer(ws1)
const requestor = agent2.peer(ws2)
Expand Down
11 changes: 7 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ pnpm install
npx simple-git-hooks
```

## Deployments
## Deployments

### Access API

There's 3 environments prodution, staging and dev. The URLs are:
- Prodution: https://access-api.web3.storage
- Staging: https://access-api-staging.web3.storage

- Prodution: https://access.web3.storage
- Staging: https://w3access-staging.protocol-labs.workers.dev
- Dev: https://w3access-dev.protocol-labs.workers.dev

The history and the current deployed commits can be checked at https://github.com/web3-storage/w3-protocol/deployments. The deployed commit hash for each environment can also be checked directly in the API .ie [`https://access-api.web3.storage/version`](https://access-api.web3.storage/version).
The history and the current deployed commits can be checked at https://github.com/web3-storage/w3-protocol/deployments. The deployed commit hash for each environment can also be checked directly in the API .ie [`https://access.web3.storage/version`](https://access.web3.storage/version).

#### Deployment Flow

1. Each PR deploys to `dev`, we should add support for unique deployments and remove `dev` completely.
2. Each Access API **Release** PR deploys to `staging` every time the CI runs for that PR.
- Note: Any change to the folder `packages/access-api` will update the Access API release PR and trigger a new deployment to `staging`.
3. After merging an Access API release PR to `main` CI will deploy to `prodution`.

#### Reverting a bad deployment

When something bad is deployed to any environment it can be reverted by going to the [Manual Deploy Workflow](https://github.com/web3-storage/w3-protocol/actions/workflows/manual.yml) select a branch or tag, the package (in this case `access-api`) and the environment and run the workflow.