Skip to content

Commit 4bd910b

Browse files
authored
Merge pull request #22 from paritytech/rfc/0015-get-user-id
RFC-0015: Get User Primary DotNS Name
2 parents c7e0ec8 + b81f945 commit 4bd910b

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

docs/rfcs/0015-get-user-id.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# RFC-0015: Get User Primary DotNS Name
2+
3+
| | |
4+
| --------------- | ------------------------------------------------------------------------------------- |
5+
| **Start Date** | 2026-04-27 |
6+
| **Description** | Host API call returning the user's primary DotNS username, plus account-type cleanup |
7+
| **Authors** | Valentin Sergeev |
8+
9+
## Summary
10+
11+
A new host call, `host_get_user_id`, returns the user's primary DotNS username scoped to the calling product. The existing `Account` type is split into `ProductAccount` (no name) and `LegacyAccount` (with name) so that the presence of a `name` field always means "user-chosen label."
12+
13+
Supersedes RFC-0010, which was merged without review.
14+
15+
## Motivation
16+
17+
Products need a way to refer to the user by a human-readable name. Today the only username-shaped field is `Account.name`, returned by both `host_account_get` (product accounts) and `host_get_legacy_accounts` (imported accounts). This is ambiguous: product accounts are protocol-derived and have no user-chosen label, so whatever a host puts in `Account.name` for them is host-defined. Legacy accounts, on the other hand, *do* carry a meaningful user-chosen label and need to keep one.
18+
19+
RFC-0010 tried to solve the username need by returning a full root account `{ public_key, name }`. That leaks more than the original requirement ("return the primary username") and re-couples username retrieval to account retrieval. This RFC realigns to the original requirement.
20+
21+
## Stakeholders
22+
23+
Product developers (consumers), host / Account Holder implementors (own the user-to-username mapping and consent UX), end users (control disclosure).
24+
25+
## Explanation
26+
27+
### `host_get_user_id`
28+
29+
```rust
30+
fn host_get_user_id() -> Result<GetUserIdResponse, GetUserIdErr>
31+
32+
struct GetUserIdResponse {
33+
/// The user's primary DotNS username scoped to the calling product.
34+
primary_username: DotNsIdentifier
35+
}
36+
37+
enum GetUserIdErr {
38+
/// User denied the disclosure request.
39+
PermissionDenied,
40+
/// User is not logged in.
41+
NotConnected,
42+
Unknown(GenericErr)
43+
}
44+
```
45+
46+
`DotNsIdentifier` is the existing API type — no new identifier shape.
47+
48+
Behavior:
49+
50+
- **Connection precedence.** No connected account → `NotConnected` without prompting. `NotConnected` strictly precedes `PermissionDenied`.
51+
- **Consent.** If connected and not previously granted, the host prompts using the existing permission model (one-time vs persistent). On denial → `PermissionDenied`.
52+
- **Source-agnostic and host-chosen.** The host picks what counts as primary for this product (lite username, full username, custom — products MUST NOT assume). When the user is connected, the host is guaranteed to be able to pick one.
53+
- **Per-product scope.** Whether two products see the same identifier is a host implementation choice. Simple hosts will return the same to all; sophisticated hosts MAY let users pick distinct primaries per product.
54+
- **Per-call freshness, no revocation.** Each call reflects current host state; if the user changes their primary, subsequent calls return the new value. Once disclosed, a value cannot be retracted from the product.
55+
- **Sync semantics.** The signature is synchronous in the language-agnostic protocol; concrete bindings may expose it as `Promise`/`Future`/etc.
56+
57+
### Account type split
58+
59+
```rust
60+
/// Protocol-derived, product-scoped. No user-chosen label.
61+
pub struct ProductAccount {
62+
pub public_key: PublicKey,
63+
}
64+
65+
/// User-imported into the Account Holder. May carry a user-chosen label.
66+
pub struct LegacyAccount {
67+
pub public_key: PublicKey,
68+
pub name: Option<String>,
69+
}
70+
71+
fn host_account_get(
72+
product_account_id: ProductAccountId
73+
) -> Result<ProductAccount, RequestCredentialsError>
74+
75+
fn host_get_legacy_accounts() -> Result<Vec<LegacyAccount>, RequestCredentialsError>
76+
```
77+
78+
The rename `host_get_non_product_accounts` → `host_get_legacy_accounts` already shipped in v0.6v0.7; only the return type changes here.
79+
80+
After this RFC, the three identity concerns separate: `host_get_user_id` for "who is the user", `host_account_get` for product-scoped signing key, `host_get_legacy_accounts` for user-imported accounts (with their labels).
81+
82+
## Drawbacks
83+
84+
- **Privacy surface.** A primary username is identifying and persistent. Once disclosed, a product may cache it indefinitely; the consent prompt is the only protocol-level mitigation. Products needing stronger guarantees should use contextual aliases instead.
85+
- **"No primary" eliminated by fiat.** If a user is connected, the host MUST be able to pick a primary; otherwise it must treat the state as `NotConnected`. This pushes complexity into the connection-status model in exchange for a smaller error surface.
86+
87+
## Compatibility
88+
89+
Breaking change:
90+
91+
1. `host_account_get` no longer returns `name`. Products reading it today must migrate to `host_get_user_id` (or stop reading it).
92+
2. `host_get_legacy_accounts` element type changes from `Account` to `LegacyAccount` — mechanical rename in typed bindings.
93+
94+
**Alternative considered (rejected): keep `Account`, return `name: None` from `host_account_get`.** Wire-compatible, but preserves the exact semantic confusion this RFC removes — `Account.name` remains reachable from product-account paths and a future host could re-populate it with host-defined values. Splitting the types makes the misuse unrepresentable.
95+
96+
## Prior Art
97+
98+
- **RFC-0010** (Host API root account access) — superseded. Returned `{ public_key, name }`, coupling username retrieval to account retrieval.
99+
- **v0.6v0.7 migration**established the "legacy account" vocabulary.
100+
- **`host_account_get_alias`**privacy-preserving alternative when a user-readable handle is not needed.
101+
102+
## Future Directions
103+
104+
- **`host_user_id_subscribe`**push updates when the user changes their primary username, avoiding re-polling.

docs/rfcs/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ created: 2026-03-13
1919
| 0010 | [Root account access Host API](0010-get-root-account.md) | accepted | @johnthecat | [#126](https://github.com/paritytech/triangle-js-sdks/pull/126) |
2020
| 0010 | [W3S Allowance Management](0010-allowance.md) | draft | @valentunn ||
2121
| 0011 | [Simple Group Chat](0011-simple-group-chat.md) | draft | @filvecchiato | [#131](https://github.com/paritytech/triangle-js-sdks/pull/131) |
22+
| 0015 | [Get User Primary DotNS Name](0015-get-user-id.md) | draft | @valentunn | [#144](https://github.com/paritytech/triangle-js-sdks/pull/144) |
2223
| 0019 | [Scheduled Push Notifications](0019-scheduled-notifications.md) | draft | @johnthecat | |

0 commit comments

Comments
 (0)