Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions js/packages/truapi-host/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export interface ProductRuntimeConfig {
/** Bulletin-chain genesis hash. */
genesisHash: string | Uint8Array;
};
/** Asset Hub configuration used to resolve session usernames from dotNS. */
assetHub: {
/** Asset Hub genesis hash. */
genesisHash: string | Uint8Array;
};
/** Wallet pairing configuration. */
pairing: {
/** URI scheme used for wallet pairing deeplinks. */
Expand Down
4 changes: 4 additions & 0 deletions js/packages/truapi-host/src/web/worker-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ function runtimeConfig(
genesisHash:
"0xbbcccc1cbe333151b8ed63b17e9e0dec61ee53b57296f1fbe2d161ae3e6fb4dc",
},
assetHub: {
genesisHash:
"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
},
pairing: {
deeplinkScheme: "polkadotapp",
},
Expand Down
13 changes: 8 additions & 5 deletions playground/src/lib/example-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Observable } from "rxjs";
import { err, ok, type Result } from "neverthrow";
import { PASEO_NEXT_V2_INDIVIDUALITY } from "@parity/truapi";
import { PASEO_NEXT_V2_ASSET_HUB } from "@parity/truapi";
import {
AccountId,
Blake2128Concat,
Expand Down Expand Up @@ -51,7 +51,10 @@ export type BuildCreateTransactionPayload = (opts: {
callData: HexString;
}) => Promise<Result<ProductAccountTxPayload, Error>>;

const usernameOwnerOfStorage = Storage("Resources")("UsernameOwnerOf", [
// Lite usernames are keyed by their dotted label ("alice.01") on the Asset
// Hub gateway pallet. Full-person ownership lives contract-side as H160 only.
// Name→account resolution therefore covers lite usernames.
const liteLabelOwnerStorage = Storage("DotnsGateway")("LiteLabelOwner", [
Bytes(),
Blake2128Concat,
]);
Expand Down Expand Up @@ -113,7 +116,7 @@ export function createAccountIdForDotNsUsername(
return err(new Error("DotNS username is empty"));
}

const key = usernameOwnerOfStorage.enc(
const key = liteLabelOwnerStorage.enc(
new TextEncoder().encode(dotNsUsername),
) as HexString;

Expand All @@ -137,7 +140,7 @@ export function createAccountIdForDotNsUsername(
switch (item.tag) {
case "Initialized": {
const result = await truapi.chain.getHeadStorage({
genesisHash: PASEO_NEXT_V2_INDIVIDUALITY.genesis,
genesisHash: PASEO_NEXT_V2_ASSET_HUB.genesis,
followSubscriptionId: sub.subscriptionId,
hash: item.value.finalizedBlockHashes[0],
items: [{ key, queryType: "Value" }],
Expand Down Expand Up @@ -188,7 +191,7 @@ export function createAccountIdForDotNsUsername(
const sub = truapi.chain
.followHeadSubscribe({
request: {
genesisHash: PASEO_NEXT_V2_INDIVIDUALITY.genesis,
genesisHash: PASEO_NEXT_V2_ASSET_HUB.genesis,
withRuntime: false,
},
})
Expand Down
1 change: 1 addition & 0 deletions rust/crates/truapi-host-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ truapi-server = { path = "../truapi-server" }
anyhow = "1"
arboard = { version = "3.6.1", default-features = false }
async-trait = "0.1"
base64 = "0.22"
bip39 = { version = "2", features = ["rand"] }
clap = { version = "4", features = ["derive", "env"] }
crossterm = { version = "0.29", features = ["event-stream"] }
Expand Down
23 changes: 12 additions & 11 deletions rust/crates/truapi-host-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,21 +391,22 @@ cleans up a unique temporary Unix socket.

## Scope / gaps

- **Chain methods** route to real `wss://` nodes from the selected `--network`
when `E2E_LIVE_CHAIN=1`; off by default. A rustls crypto provider is
installed at startup for the TLS connections.
- **Chain methods** route to real `wss://` nodes from the selected `--network`.
People, Bulletin, and Asset Hub are host-required and always enabled.
`E2E_LIVE_CHAIN=1` additionally enables any preset route marked optional. A
rustls crypto provider is installed at startup for the TLS connections.
- **Ring-VRF product-account aliases and proofs** are implemented by the
signing host via the `verifiable` crate (`get_account_alias` and
`create_account_proof`).
- **`get_user_id`** resolves the signing account's username from People-chain
`Resources.Consumers`. Auto-managed signing accounts register fresh lite
usernames via the identity backend (`src/attestation.rs`); first registration
is backend-async and can take minutes (ring onboarding). `truapi-host
identity-check --mnemonic <m>` probes which derivation carries a username.
- **`get_user_id`** resolves the signing account's username from the dotNS
contracts on Asset Hub. Auto-managed signing accounts register fresh lite
usernames via the identity backend (`src/attestation.rs`). First registration
is backend-async and can take minutes, waiting on ring onboarding.
`truapi-host identity-check --mnemonic <m>` probes which derivation carries a
username.
- `set_statement_store_account` and Bulletin long-term-storage resource
allocation are implemented over SSO on native headless hosts.
- Everything else the browser host exercises passes: signing (raw, payload,
create-transaction, and their legacy variants), statement store, entropy,
aliases, preimage, storage, permissions, notifications, theme, system, chain
(with `E2E_LIVE_CHAIN=1`), and user id, subject to live chain availability
and allowance-slot capacity.
aliases, preimage, storage, permissions, notifications, theme, system, chain,
and user id, subject to live chain availability and allowance-slot capacity.
38 changes: 28 additions & 10 deletions rust/crates/truapi-host-cli/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ as the paired path.
`truapi-host-cli` owns:

- argument and slash-command parsing;
- the single supported network preset;
- the supported network presets (`paseo-next-v2`, `previewnet`);
- local signer selection and onboarding;
- local persistence and account-store locking;
- approvals and `--auto-accept`;
Expand Down Expand Up @@ -198,7 +198,8 @@ Commands:
| --- | --- |
| `pairing-host` | Run the seedless product-facing host. |
| `signing-host` | Run the wallet-local signing host. |
| `identity-check` | Probe People-chain identity records for a mnemonic. |
| `identity-check` | Probe dotNS identity records on Asset Hub for a mnemonic. |
| `register-name` | Register a full-person username via `DotnsGateway.register_name`. |
| `alloc-check` | Inspect or submit Statement Store allowance registration. |

### 4.1 Global logging option
Expand Down Expand Up @@ -230,7 +231,7 @@ truapi-host pairing-host [options]
| `--product-id <id>` | `headless-playground.dot` | Initial product scope. |
| `--frame-listen <socket>` | none | Opt into a TCP product WebSocket listener. When omitted, use a private per-process Unix socket. Port `0` selects an available TCP port. |
| `--base-path <path>` | section 12.1 | Root for network, identity, core, script, and product state. |
| `--network <preset>` | `paseo-next-v2` | Select the complete endpoint/genesis preset. |
| `--network <preset>` | `paseo-next-v2` | Select the complete endpoint/genesis preset (`paseo-next-v2`, `previewnet`). |
| `--auto-accept` | off | Approve platform confirmations automatically. |

Without `--script`, both stdin and stdout must be terminals. The command enters
Expand Down Expand Up @@ -268,7 +269,7 @@ truapi-host signing-host [options] [exec '<slash-command>']
| `--session <name>` | remembered session | Restore or create a managed session. |
| `--lite-username-prefix <prefix>` | session-derived | Prefix for newly generated Lite username bases. |
| `--base-path <path>` | section 12.1 | Root for account, session, core, script, and product state. |
| `--network <preset>` | `paseo-next-v2` | Select the complete endpoint/genesis preset. |
| `--network <preset>` | `paseo-next-v2` | Select the complete endpoint/genesis preset (`paseo-next-v2`, `previewnet`). |
| `--frame-listen <socket>` | none | Opt into a TCP product WebSocket listener. When omitted, use a private per-process Unix socket. Port `0` is allowed. |
| `--auto-accept` | off | Approve platform confirmations automatically. |

Expand Down Expand Up @@ -797,14 +798,27 @@ A new auto account:
4. chooses `auto-<n>` as its local name;
5. tries up to eight available Lite username bases;
6. saves a pending account record;
7. builds and submits identity-backend registration proofs;
8. polls `Resources.Consumers` for the final `name.discriminator`;
7. builds and submits identity-backend registration proofs, including the dotNS
gateway reservation signature timestamped with Asset Hub chain time;
8. polls the dotNS contracts on Asset Hub for the final `name.discriminator`;
9. waits for inclusion in a LitePeople ring; and
10. marks and saves the account as attested.

Identity and ring polling each allow 10 attempts with four seconds between
attempts. Identity-backend HTTP clients use a 30-second timeout.

The backend's username routes are bearer-gated. Unless
`HOST_CLI_IDENTITY_BACKEND_TOKEN` supplies one, the CLI mints an access token
once per process. It takes a challenge from `auth/challenges`. It answers
`auth/token` with an sr25519 proof over
`SHA256(challenge || clientId || SHA256(body))`, signed by a throwaway keypair.

The token's subject only identifies the calling app instance. The username claim
carries its own candidate account. A fresh subject per run therefore stays clear
of the backend's per-subject device gate and rate limit. Availability answers are
read from both wire shapes: the `{_tag, value: {base: {status}}}` record and the
flat `{base: "AVAILABLE"}` map.

The default Lite username prefix is `headless`. For a non-default session, the
prefix is its lowercase letters with digits and separators removed; a name
with no letters becomes `session`. `--lite-username-prefix` overrides this and
Expand Down Expand Up @@ -1097,8 +1111,9 @@ v0.1 supports only `paseo-next-v2`.

There are no public endpoint override flags.

People and Bulletin routes are always enabled because host internals require
them. Asset Hub routing is enabled only when `E2E_LIVE_CHAIN=1`.
People, Bulletin, and Asset Hub routes are always enabled because host
internals require them. `E2E_LIVE_CHAIN=1` additionally enables any preset
route marked optional.

The all-zero SSO sentinel and every genesis hash not present in the active
route map fall back to the People RPC.
Expand Down Expand Up @@ -1148,7 +1163,7 @@ surface.
| Service | Implemented behavior |
| --- | --- |
| Account | Connection status, product accounts, aliases, proofs, empty legacy-account list, user id, and login. |
| Chain | chainHead-v1 follow/header/body/storage/call/unpin/continue/stop, chain spec queries, transaction broadcast/stop. Asset Hub needs `E2E_LIVE_CHAIN=1`. |
| Chain | chainHead-v1 follow/header/body/storage/call/unpin/continue/stop, chain spec queries, transaction broadcast/stop. People, Bulletin, and Asset Hub all route to the preset's live nodes. |
| Entropy | Product-scoped deterministic entropy from the active account/session. |
| Local Storage | Persistent product-scoped read, write, and clear. |
| Notifications | In-process immediate/scheduled delivery and cancellation with transcript events. |
Expand Down Expand Up @@ -1490,12 +1505,15 @@ ended. This preserves the child status but bypasses later Rust destructors.
| `RUST_LOG` | Full startup tracing filter. |
| `TRUAPI_HOST_BASE_PATH` | Default `--base-path`. |
| `HOST_CLI_SIGNER_MNEMONIC` | Signing, identity, and allowance mnemonic input. |
| `HOST_CLI_IDENTITY_BACKEND_BASE` | Identity backend base URL override, including `/api/v1`, for instance a local backend. Chain endpoints stay on the preset. |
| `HOST_CLI_IDENTITY_BACKEND_TOKEN` | Bearer token for the identity backend's username routes. Unset, the CLI mints one itself through the backend's `auth/challenges` → `auth/token` sr25519 handshake with a throwaway keypair. |
| `HOST_CLI_DOTNS_POP_CONTROLLER` | `DotnsPopController` H160 override, skipping on-chain discovery. Required on networks whose deployed dispatcher exposes no target getter. On paseo-next-v2 that address is `0x1c858C31497a7715C0D56A11208feB6b74FaB2aB`. |
| `XDG_STATE_HOME` | Preferred default state parent. |
| `HOME` | Fallback default state parent. |
| `VISUAL` | Preferred script editor. |
| `EDITOR` | Fallback script editor. |
| `TRUAPI_HOST_RUNNER` | Override `js/runner.ts`. |
| `E2E_LIVE_CHAIN` | Value `1` enables optional Asset Hub routing. |
| `E2E_LIVE_CHAIN` | Value `1` enables optional live-chain routing. Every preset route is currently host-required, so this is a no-op. |
| `NO_COLOR` | Disable CLI semantic colors and battery reporter color. |
| `COLORFGBG` | Infer TUI background color. |
| `COLORTERM` | Select true-color TUI rendering. |
Expand Down
5 changes: 3 additions & 2 deletions rust/crates/truapi-host-cli/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ async fn ensure_record_ready(
record.attested = true;
} else {
record.lite_username =
attestation::registered_lite_username(network.people_ws, &identity.entropy)
attestation::registered_lite_username(network.asset_hub_ws, &identity.entropy)
.await
.with_context(|| format!("resolve Lite username for account {}", record.name))?;
}
Expand All @@ -447,9 +447,10 @@ async fn attest_record(network: NetworkConfig, record: &AccountRecord) -> Result
let entropy = mnemonic_entropy(&record.mnemonic)?;
let lite_username = attestation::attest(&attestation::AttestConfig {
backend_base: network.identity_backend_base.to_string(),
people_ws: network.people_ws.to_string(),
asset_hub_ws: network.asset_hub_ws.to_string(),
entropy,
username_base: record.lite_username.clone(),
reserved_username: None,
})
.await
.with_context(|| format!("attest account {}", record.name))?;
Expand Down
Loading