Skip to content

Recover an identity

By the end of this page Alice has a recovery code, and with it gets her name, spaces and contacts back on a new laptop.

If every device is lost, the identity is lost with them, unless it has a recovery credential: a key kept outside every device. A recovery code is one such key, written as twelve words. Making it adds the key to the identity’s group as a member, the same way a device is added, so the key can admit a new device later.

Recovery goes through EDGE. The new device asks EDGE for a challenge, signs it with the key derived from the words, and EDGE checks the signature against the identity’s recovery credentials before it admits the device. The new device is a new member of the same identity, with the same DID, and syncs its spaces from EDGE and from anyone online.

HALO shows a recovery code once and keeps only its public half, and it will not revoke the last code you have: make the new one first. A passkey works the same way; see Passkey recovery.

  1. Make a recovery code. Identity.createRecoveryCredential makes the key, adds it to the identity, and returns the words. Make a recovery code on the Recovery tab calls this.

    src/app/Recovery.tsx
    import * as
    import Effect
    Effect
    from 'effect/Effect';
    2 collapsed lines
    import
    (alias) namespace React
    import React
    React
    , {
    function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    } from 'react';
    import {
    import Identity
    Identity
    } from '@dxos/halo';
    14 collapsed lines
    import {
    const useCredentials: () => readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ,
    const useHaloServices: () => Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    } from '@dxos/halo-react';
    import {
    const Copyable: ({ value }: {
    value: string;
    }) => React.JSX.Element
    Copyable
    } from './ui.tsx';
    /** Your recovery codes: make one, see the ones that work, and revoke one. */
    export const
    const Recovery: () => React.JSX.Element

    Your recovery codes: make one, see the ones that work, and revoke one.

    Recovery
    = () => {
    const
    const services: Context<HaloServices>
    services
    =
    function useHaloServices(): Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    ();
    /** The recovery credentials that still work, re-rendering on each change. */
    const
    const codes: {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    The recovery credentials that still work, re-rendering on each change.

    codes
    =
    function useCredentials(): readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ().
    ReadonlyArray<{ readonly type: string; readonly id?: string | undefined; readonly issuanceDate?: Date | undefined; readonly recovery?: { readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown"; readonly revoked: boolean; readonly lookupKey?: string | undefined; readonly label?: string | undefined; } | undefined; }>.flatMap<{
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }, undefined>(callback: (this: undefined, value: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }, index: number, array: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }[]) => {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    } | readonly {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[], thisArg?: undefined): {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1.

    @param ― callback A function that accepts up to three arguments. The flatMap method calls the callback function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the callback function. If thisArg is omitted, undefined is used as the this value.

    flatMap
    (({
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }) =>
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    && !
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    .
    revoked: boolean

    Whether an IdentityRecoveryRevoked assertion cancels this credential.

    revoked
    ? [{ ...
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }] : [],
    );
    const [
    const created: string | undefined
    created
    ,
    const setCreated: React.Dispatch<React.SetStateAction<string | undefined>>
    setCreated
    ] =
    useState<string>(): [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    <string>();
    const [
    const error: string | undefined
    error
    ,
    const setError: React.Dispatch<React.SetStateAction<string | undefined>>
    setError
    ] =
    useState<string>(): [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    <string>();
    /** Makes a recovery code, and shows it. HALO returns it only this once. */
    const
    const handleCreate: () => Promise<void>

    Makes a recovery code, and shows it. HALO returns it only this once.

    handleCreate
    = () =>
    import Effect
    Effect
    .
    const gen: <Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>, string | undefined>(f: () => Generator<Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>, string | undefined, never>) => Effect.Effect<string | undefined, IdentityError, Identity.Service> (+1 overload)

    Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

    When to use

    Use when you want to write effectful code that looks and behaves like synchronous code, while still handling asynchronous tasks, errors, and complex control flow such as loops and conditions.

    Generator functions work similarly to async/await but keep errors, requirements, and interruption in the Effect type. You can yield* values from effects and return the final result at the end.

    Example (Sequencing effects with generators)

    import { Data, Effect } from "effect"
    class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
    const addServiceCharge = (amount: number) => amount + 1
    const applyDiscount = (
    total: number,
    discountRate: number
    ): Effect.Effect<number, DiscountRateError> =>
    discountRate === 0
    ? Effect.fail(new DiscountRateError())
    : Effect.succeed(total - (total * discountRate) / 100)
    const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
    const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
    export const program = Effect.gen(function*() {
    const transactionAmount = yield* fetchTransactionAmount
    const discountRate = yield* fetchDiscountRate
    const discountedAmount = yield* applyDiscount(
    transactionAmount,
    discountRate
    )
    const finalAmount = addServiceCharge(discountedAmount)
    return `Final amount to charge: ${finalAmount}`
    })
    await Effect.runPromise(program) // => "Final amount to charge: 96"

    @stability ― stable

    @category ― constructors

    @since ― 2.0.0

    gen
    (function* () {
    const {
    const recoveryCode: string | undefined
    recoveryCode
    } = yield*
    import Identity
    Identity
    .
    const createRecoveryCredential: (options?: Identity.RecoveryCredentialOptions) => Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>

    Write a recovery credential (requires

    Service

    ).

    createRecoveryCredential
    ();
    return
    const recoveryCode: string | undefined
    recoveryCode
    ;
    })
    .
    Pipeable.pipe<Effect.Effect<string | undefined, IdentityError, Identity.Service>, Effect.Effect<string | undefined, IdentityError, never>, Promise<string | undefined>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string | undefined, IdentityError, Identity.Service>) => Effect.Effect<string | undefined, IdentityError, never>, bc: (_: Effect.Effect<string | undefined, IdentityError, never>) => Promise<string | undefined>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    )
    .
    Promise<string | undefined>.then<void, never>(onfulfilled?: ((value: string | undefined) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>

    Attaches callbacks for the resolution and/or rejection of the Promise.

    @param ― onfulfilled The callback to execute when the Promise is resolved.

    @param ― onrejected The callback to execute when the Promise is rejected.

    @returns ― A Promise for the completion of which ever callback is executed.

    then
    ((
    code: string | undefined
    code
    ) => {
    const setError: (value: React.SetStateAction<string | undefined>) => void
    setError
    (
    var undefined
    undefined
    );
    const setCreated: (value: React.SetStateAction<string | undefined>) => void
    setCreated
    (
    code: string | undefined
    code
    );
    });
  2. List your codes. useCredentials returns the identity’s credentials and re-renders when one is written. It follows Identity.credentials. A recovery credential carries its kind, a label, the date it was made, a lookup key and whether it was revoked. The Recovery tab lists the ones still active.

    src/app/Recovery.tsx
    import {
    const useCredentials: () => readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ,
    const useHaloServices: () => Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    } from '@dxos/halo-react';
    6 collapsed lines
    import {
    const Copyable: ({ value }: {
    value: string;
    }) => React.JSX.Element
    Copyable
    } from './ui.tsx';
    /** Your recovery codes: make one, see the ones that work, and revoke one. */
    export const
    const Recovery: () => React.JSX.Element

    Your recovery codes: make one, see the ones that work, and revoke one.

    Recovery
    = () => {
    const
    const services: Context<HaloServices>
    services
    =
    function useHaloServices(): Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    ();
    /** The recovery credentials that still work, re-rendering on each change. */
    const
    const codes: {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    The recovery credentials that still work, re-rendering on each change.

    codes
    =
    function useCredentials(): readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ().
    ReadonlyArray<{ readonly type: string; readonly id?: string | undefined; readonly issuanceDate?: Date | undefined; readonly recovery?: { readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown"; readonly revoked: boolean; readonly lookupKey?: string | undefined; readonly label?: string | undefined; } | undefined; }>.flatMap<{
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }, undefined>(callback: (this: undefined, value: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }, index: number, array: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }[]) => {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    } | readonly {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[], thisArg?: undefined): {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1.

    @param ― callback A function that accepts up to three arguments. The flatMap method calls the callback function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the callback function. If thisArg is omitted, undefined is used as the this value.

    flatMap
    (({
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }) =>
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    && !
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    .
    revoked: boolean

    Whether an IdentityRecoveryRevoked assertion cancels this credential.

    revoked
    ? [{ ...
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }] : [],
    );
  3. Revoke a code. Identity.revokeRecoveryCredential takes the lookup key. Revoke a code you think someone has seen.

    src/app/Recovery.tsx
    import * as
    import Effect
    Effect
    from 'effect/Effect';
    2 collapsed lines
    import
    (alias) namespace React
    import React
    React
    , {
    function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    } from 'react';
    import {
    import Identity
    Identity
    } from '@dxos/halo';
    26 collapsed lines
    import {
    const useCredentials: () => readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ,
    const useHaloServices: () => Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    } from '@dxos/halo-react';
    import {
    const Copyable: ({ value }: {
    value: string;
    }) => React.JSX.Element
    Copyable
    } from './ui.tsx';
    /** Your recovery codes: make one, see the ones that work, and revoke one. */
    export const
    const Recovery: () => React.JSX.Element

    Your recovery codes: make one, see the ones that work, and revoke one.

    Recovery
    = () => {
    const
    const services: Context<HaloServices>
    services
    =
    function useHaloServices(): Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    ();
    /** The recovery credentials that still work, re-rendering on each change. */
    const
    const codes: {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    The recovery credentials that still work, re-rendering on each change.

    codes
    =
    function useCredentials(): readonly Identity.Credential[]

    Returns the local identity's HALO credentials. Reactive: re-renders when credentials are written. Replaces @dxos/react-client's useCredentials.

    useCredentials
    ().
    ReadonlyArray<{ readonly type: string; readonly id?: string | undefined; readonly issuanceDate?: Date | undefined; readonly recovery?: { readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown"; readonly revoked: boolean; readonly lookupKey?: string | undefined; readonly label?: string | undefined; } | undefined; }>.flatMap<{
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }, undefined>(callback: (this: undefined, value: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }, index: number, array: {
    readonly type: string;
    readonly id?: string | undefined;
    readonly issuanceDate?: Date | undefined;
    readonly recovery?: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined;
    }[]) => {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    } | readonly {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[], thisArg?: undefined): {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1.

    @param ― callback A function that accepts up to three arguments. The flatMap method calls the callback function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the callback function. If thisArg is omitted, undefined is used as the this value.

    flatMap
    (({
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }) =>
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    } | undefined
    recovery
    && !
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    .
    revoked: boolean

    Whether an IdentityRecoveryRevoked assertion cancels this credential.

    revoked
    ? [{ ...
    recovery: {
    readonly kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    readonly revoked: boolean;
    readonly lookupKey?: string | undefined;
    readonly label?: string | undefined;
    }
    recovery
    ,
    issuanceDate: Date | undefined
    issuanceDate
    }] : [],
    );
    const [
    const created: string | undefined
    created
    ,
    const setCreated: React.Dispatch<React.SetStateAction<string | undefined>>
    setCreated
    ] =
    useState<string>(): [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    <string>();
    const [
    const error: string | undefined
    error
    ,
    const setError: React.Dispatch<React.SetStateAction<string | undefined>>
    setError
    ] =
    useState<string>(): [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    <string>();
    /** Makes a recovery code, and shows it. HALO returns it only this once. */
    const
    const handleCreate: () => Promise<void>

    Makes a recovery code, and shows it. HALO returns it only this once.

    handleCreate
    = () =>
    import Effect
    Effect
    .
    const gen: <Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>, string | undefined>(f: () => Generator<Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>, string | undefined, never>) => Effect.Effect<string | undefined, IdentityError, Identity.Service> (+1 overload)

    Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

    When to use

    Use when you want to write effectful code that looks and behaves like synchronous code, while still handling asynchronous tasks, errors, and complex control flow such as loops and conditions.

    Generator functions work similarly to async/await but keep errors, requirements, and interruption in the Effect type. You can yield* values from effects and return the final result at the end.

    Example (Sequencing effects with generators)

    import { Data, Effect } from "effect"
    class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
    const addServiceCharge = (amount: number) => amount + 1
    const applyDiscount = (
    total: number,
    discountRate: number
    ): Effect.Effect<number, DiscountRateError> =>
    discountRate === 0
    ? Effect.fail(new DiscountRateError())
    : Effect.succeed(total - (total * discountRate) / 100)
    const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
    const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
    export const program = Effect.gen(function*() {
    const transactionAmount = yield* fetchTransactionAmount
    const discountRate = yield* fetchDiscountRate
    const discountedAmount = yield* applyDiscount(
    transactionAmount,
    discountRate
    )
    const finalAmount = addServiceCharge(discountedAmount)
    return `Final amount to charge: ${finalAmount}`
    })
    await Effect.runPromise(program) // => "Final amount to charge: 96"

    @stability ― stable

    @category ― constructors

    @since ― 2.0.0

    gen
    (function* () {
    const {
    const recoveryCode: string | undefined
    recoveryCode
    } = yield*
    import Identity
    Identity
    .
    const createRecoveryCredential: (options?: Identity.RecoveryCredentialOptions) => Effect.Effect<{
    recoveryCode?: string;
    }, IdentityError, Identity.Service>

    Write a recovery credential (requires

    Service

    ).

    createRecoveryCredential
    ();
    return
    const recoveryCode: string | undefined
    recoveryCode
    ;
    })
    .
    Pipeable.pipe<Effect.Effect<string | undefined, IdentityError, Identity.Service>, Effect.Effect<string | undefined, IdentityError, never>, Promise<string | undefined>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string | undefined, IdentityError, Identity.Service>) => Effect.Effect<string | undefined, IdentityError, never>, bc: (_: Effect.Effect<string | undefined, IdentityError, never>) => Promise<string | undefined>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    )
    .
    Promise<string | undefined>.then<void, never>(onfulfilled?: ((value: string | undefined) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>

    Attaches callbacks for the resolution and/or rejection of the Promise.

    @param ― onfulfilled The callback to execute when the Promise is resolved.

    @param ― onrejected The callback to execute when the Promise is rejected.

    @returns ― A Promise for the completion of which ever callback is executed.

    then
    ((
    code: string | undefined
    code
    ) => {
    const setError: (value: React.SetStateAction<string | undefined>) => void
    setError
    (
    var undefined
    undefined
    );
    const setCreated: (value: React.SetStateAction<string | undefined>) => void
    setCreated
    (
    code: string | undefined
    code
    );
    });
    /** Revokes the recovery credential with `lookupKey`. */
    const
    const handleRevoke: (lookupKey: string) => Promise<void>

    Revokes the recovery credential with lookupKey.

    handleRevoke
    = (
    lookupKey: string
    lookupKey
    : string) =>
    import Identity
    Identity
    .
    const revokeRecoveryCredential: (lookupKey: string) => Effect.Effect<void, IdentityError, Identity.Service>

    Revoke a recovery credential by lookup key (requires

    Service

    ).

    revokeRecoveryCredential
    (
    lookupKey: string
    lookupKey
    )
    .
    Pipeable.pipe<Effect.Effect<void, IdentityError, Identity.Service>, Effect.Effect<void, IdentityError, never>, Promise<void>>(this: Effect.Effect<void, IdentityError, Identity.Service>, ab: (_: Effect.Effect<void, IdentityError, Identity.Service>) => Effect.Effect<void, IdentityError, never>, bc: (_: Effect.Effect<void, IdentityError, never>) => Promise<void>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    )
    .
    Promise<void>.then<void, void>(onfulfilled?: ((value: void) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>

    Attaches callbacks for the resolution and/or rejection of the Promise.

    @param ― onfulfilled The callback to execute when the Promise is resolved.

    @param ― onrejected The callback to execute when the Promise is rejected.

    @returns ― A Promise for the completion of which ever callback is executed.

    then
    (
    () =>
    const setError: (value: React.SetStateAction<string | undefined>) => void
    setError
    (
    var undefined
    undefined
    ),
    (
    error: any
    error
    ) =>
    const setError: (value: React.SetStateAction<string | undefined>) => void
    setError
    (
    const codes: {
    issuanceDate: Date | undefined;
    kind: "passkey" | "recovery-code" | "oauth" | "unknown";
    revoked: boolean;
    lookupKey?: string | undefined;
    label?: string | undefined;
    }[]

    The recovery credentials that still work, re-rendering on each change.

    codes
    .
    Array<T>.length: number

    Gets or sets the length of the array. This is a number one higher than the highest index in the array.

    length
    === 1
    ? 'HALO will not revoke your last recovery code. Make another one first.'
    :
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    error: any
    error
    ),
    ),
    );
  4. Recover on a new device. On a device with no identity, Identity.recover takes the words and gets the identity back. Recover with a code calls this.

    src/app/Welcome.tsx
    import * as
    import Effect
    Effect
    from 'effect/Effect';
    2 collapsed lines
    import
    (alias) namespace React
    import React
    React
    , {
    function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    } from 'react';
    import {
    import Identity
    Identity
    } from '@dxos/halo';
    28 collapsed lines
    import {
    const useHaloServices: () => Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    } from '@dxos/halo-react';
    import { type
    type Stage = "name" | "spaces" | "invite" | "contacts" | "devices" | "recovery"

    How far a page of the learning path has built the app, in the order the path builds it.

    Stage
    ,
    const reached: (stage: Stage, at: Stage) => boolean

    Whether stage includes what at adds.

    reached
    } from '../stage.ts';
    import {
    const Guest: ({ label, join, onDone, }: {
    label: string;
    join: (code: string) => Promise<Flow>;
    onDone?: () => void;
    }) => React.JSX.Element

    The guest's side of an invitation: paste the code, then type the authorization code.

    Guest
    } from './Invitations.tsx';
    import {
    const Prompt: ({ label, action, initial, multiline, compact, autoFocus, onSubmit, }: {
    label: string;
    action: string;
    initial?: string;
    multiline?: boolean;
    compact?: boolean;
    autoFocus?: boolean;
    onSubmit: (value: string) => Promise<unknown>;
    }) => React.JSX.Element

    A field and a button, which shows the error if onSubmit fails.

    Prompt
    } from './ui.tsx';
    /** The device before it has an identity: start one, join one from another device, or recover one. */
    export const
    const Welcome: ({ stage }: {
    stage: Stage;
    }) => React.JSX.Element

    The device before it has an identity: start one, join one from another device, or recover one.

    Welcome
    = ({
    stage: Stage
    stage
    }: {
    stage: Stage
    stage
    :
    type Stage = "name" | "spaces" | "invite" | "contacts" | "devices" | "recovery"

    How far a page of the learning path has built the app, in the order the path builds it.

    Stage
    }) => {
    const
    const services: Context<HaloServices>
    services
    =
    function useHaloServices(): Context<HaloServices>

    Returns the HALO services context. Throws if no

    HaloProvider

    is above in the tree.

    useHaloServices
    ();
    const [
    const mode: "new" | "device" | "recover"
    mode
    ,
    const setMode: React.Dispatch<React.SetStateAction<"new" | "device" | "recover">>
    setMode
    ] =
    useState<"new" | "device" | "recover">(initialState: "new" | "device" | "recover" | (() => "new" | "device" | "recover")): ["new" | "device" | "recover", React.Dispatch<React.SetStateAction<"new" | "device" | "recover">>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    <'new' | 'device' | 'recover'>('new');
    // Focusing the first field on load would scroll the page to the embed.
    const [
    const picked: boolean
    picked
    ,
    const setPicked: React.Dispatch<React.SetStateAction<boolean>>
    setPicked
    ] =
    useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)

    Returns a stateful value, and a function to update it.

    @version ― 16.8.0

    @see ― https://react.dev/reference/react/useState

    useState
    (false);
    const
    const pick: (next: "new" | "device" | "recover") => void
    pick
    = (
    next: "new" | "device" | "recover"
    next
    : typeof
    const mode: "new" | "device" | "recover"
    mode
    ) => {
    const setPicked: (value: React.SetStateAction<boolean>) => void
    setPicked
    (true);
    const setMode: (value: React.SetStateAction<"new" | "device" | "recover">) => void
    setMode
    (
    next: "new" | "device" | "recover"
    next
    );
    };
    /** Creates the identity, with this device as its first member. */
    const
    const handleStart: (name: string) => Promise<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }>

    Creates the identity, with this device as its first member.

    handleStart
    = (
    name: string
    name
    : string) =>
    import Identity
    Identity
    .
    const create: (options?: {
    displayName?: string;
    data?: Record<string, unknown>;
    deviceLabel?: string;
    }) => Effect.Effect<Identity.Info, IdentityError, Identity.Service>

    Create the local identity (requires

    Service

    ).

    create
    ({
    displayName?: string | undefined
    displayName
    :
    name: string
    name
    }).
    Pipeable.pipe<Effect.Effect<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }, IdentityError, Identity.Service>, Effect.Effect<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }, IdentityError, never>, Promise<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Promise<...>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    ,
    );
    /** Redeems the invitation `code` from your other device, and returns the flow. */
    const
    const handleJoin: (code: string) => Promise<Flow>

    Redeems the invitation code from your other device, and returns the flow.

    handleJoin
    = (
    code: string
    code
    : string) =>
    import Identity
    Identity
    .
    const join: (code: string) => Effect.Effect<Flow, IdentityError, Identity.Service>

    Redeem a device-invitation code (requires

    Service

    ).

    join
    (
    code: string
    code
    ).
    Pipeable.pipe<Effect.Effect<Flow, IdentityError, Identity.Service>, Effect.Effect<Flow, IdentityError, never>, Promise<Flow>>(this: Effect.Effect<Flow, IdentityError, Identity.Service>, ab: (_: Effect.Effect<Flow, IdentityError, Identity.Service>) => Effect.Effect<Flow, IdentityError, never>, bc: (_: Effect.Effect<Flow, IdentityError, never>) => Promise<Flow>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    );
    /** Gets the identity back with `recoveryCode`. */
    const
    const handleRecover: (recoveryCode: string) => Promise<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }>

    Gets the identity back with recoveryCode.

    handleRecover
    = (
    recoveryCode: string
    recoveryCode
    : string) =>
    import Identity
    Identity
    .
    const recover: (args: Identity.RecoverArgs) => Effect.Effect<Identity.Info, IdentityError, Identity.Service>

    Re-admit this device via a recovery credential (requires

    Service

    ).

    recover
    ({
    recoveryCode: string
    recoveryCode
    :
    recoveryCode: string
    recoveryCode
    .
    String.trim(): string

    Removes the leading and trailing white space and line terminator characters from a string.

    trim
    () }).
    Pipeable.pipe<Effect.Effect<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }, IdentityError, Identity.Service>, Effect.Effect<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }, IdentityError, never>, Promise<{
    readonly did: IdentityDid;
    readonly displayName?: string | undefined;
    readonly identityKey?: string | undefined;
    readonly data?: {
    readonly [x: string]: any;
    } | undefined;
    }>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Promise<...>): Promise<...> (+21 overloads)
    pipe
    (
    import Effect
    Effect
    .
    const provide: <HaloServices>(context: Context<HaloServices>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, HaloServices>> (+5 overloads)

    Provides dependencies to an effect using layers or a context. Use options.local to build the layer every time; by default, layers are shared between provide calls.

    Example (Providing dependencies with a layer)

    import { Context, Effect, Layer } from "effect"
    interface Database {
    readonly query: (sql: string) => Effect.Effect<string>
    }
    const Database = Context.Service<Database>("Database")
    const DatabaseLayer = Layer.succeed(Database)({
    query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
    })
    const program = Effect.gen(function*() {
    const db = yield* Database
    return yield* db.query("SELECT * FROM users")
    })
    const provided = Effect.provide(program, DatabaseLayer)
    await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

    @stability ― stable

    @category ― providing services

    @since ― 2.0.0

    provide
    (
    const services: Context<HaloServices>
    services
    ),
    import Effect
    Effect
    .
    const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>

    Executes an effect and returns the result as a Promise.

    When to use

    Use when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

    If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

    Example (Running a successful effect as a Promise)

    import { Effect } from "effect"
    await Effect.runPromise(Effect.succeed(1)) // => 1

    Example (Running effects as promises)

    //Example: Handling a Failing Effect as a Rejected Promise
    import { Effect } from "effect"
    const output: Array<unknown> = []
    await Effect.runPromise(Effect.fail("my error")).catch(() => {
    void output.push("rejected")
    })
    output // => ["rejected"]

    @see ― runPromiseExit for a version that returns an Exit type instead of rejecting.

    @stability ― stable

    @category ― running

    @since ― 2.0.0

    runPromise
    ,
    );

Open Recovery on Alice’s laptop and make a code. Make a second one, then revoke the first. Try revoking the last one too, and HALO refuses.

You can make and revoke recovery codes, and recover an identity on a new device. That is the whole contact book. From here, Core concepts explains the model behind it, and the API pages cover the rest of @dxos/halo.