add Yonexus.Protocol submodule and update architecture docs
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
|||||||
[submodule "Yonexus.Client"]
|
[submodule "Yonexus.Client"]
|
||||||
path = Yonexus.Client
|
path = Yonexus.Client
|
||||||
url = https://git.hangman-lab.top/nav/Yonexus.Client.git
|
url = https://git.hangman-lab.top/nav/Yonexus.Client.git
|
||||||
|
[submodule "Yonexus.Protocol"]
|
||||||
|
path = Yonexus.Protocol
|
||||||
|
url = https://git.hangman-lab.top/nav/Yonexus.Protocol.git
|
||||||
|
|||||||
178
ARCHITECTURE.md
178
ARCHITECTURE.md
@@ -6,13 +6,13 @@ Yonexus is a cross-instance communication system for OpenClaw.
|
|||||||
|
|
||||||
The repository `Yonexus` is the **umbrella/specification repository** for the system. It contains:
|
The repository `Yonexus` is the **umbrella/specification repository** for the system. It contains:
|
||||||
- high-level planning
|
- high-level planning
|
||||||
- protocol specification
|
- architecture documents
|
||||||
- shared architectural decisions
|
|
||||||
- references to implementation repositories as git submodules
|
- references to implementation repositories as git submodules
|
||||||
|
|
||||||
Yonexus is implemented as two separate plugins:
|
Yonexus is implemented as three repositories:
|
||||||
- `Yonexus.Server`
|
- `Yonexus.Server` — central hub plugin
|
||||||
- `Yonexus.Client`
|
- `Yonexus.Client` — client plugin
|
||||||
|
- `Yonexus.Protocol` — shared protocol specification, referenced as a submodule by both
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -23,20 +23,34 @@ Yonexus is implemented as two separate plugins:
|
|||||||
Purpose:
|
Purpose:
|
||||||
- system-level planning
|
- system-level planning
|
||||||
- architecture documents
|
- architecture documents
|
||||||
- protocol definition
|
- cross-cutting decisions that apply to both server and client
|
||||||
- shared design decisions
|
- coordination of sub-repositories via git submodules
|
||||||
- cross-repo coordination
|
|
||||||
|
|
||||||
This repository should contain:
|
This repository should contain:
|
||||||
- top-level planning docs
|
- top-level planning docs
|
||||||
- protocol docs
|
|
||||||
- feature checklists
|
|
||||||
- architecture overview
|
- architecture overview
|
||||||
- cross-cutting decisions that apply to both server and client
|
- feature checklists
|
||||||
|
- cross-cutting design rationale
|
||||||
|
|
||||||
This repository should **not** become the place where all implementation code lives.
|
It references:
|
||||||
|
- `Yonexus.Server` (submodule)
|
||||||
|
- `Yonexus.Client` (submodule)
|
||||||
|
- `Yonexus.Protocol` (submodule)
|
||||||
|
|
||||||
## 2.2 `Yonexus.Server`
|
## 2.2 `Yonexus.Protocol`
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- protocol specification (PROTOCOL.md)
|
||||||
|
- canonical JSON shape references
|
||||||
|
- shared type definitions (planned)
|
||||||
|
|
||||||
|
Referenced as a submodule by:
|
||||||
|
- `Yonexus.Server/protocol`
|
||||||
|
- `Yonexus.Client/protocol`
|
||||||
|
|
||||||
|
This is the **single source of truth** for the Yonexus protocol. Both server and client implementations must conform to the protocol defined here.
|
||||||
|
|
||||||
|
## 2.3 `Yonexus.Server`
|
||||||
|
|
||||||
Purpose:
|
Purpose:
|
||||||
- implementation of the central hub/server plugin
|
- implementation of the central hub/server plugin
|
||||||
@@ -44,7 +58,12 @@ Purpose:
|
|||||||
- server-side pairing/authentication/state tracking
|
- server-side pairing/authentication/state tracking
|
||||||
- server-side dispatch and routing behavior
|
- server-side dispatch and routing behavior
|
||||||
|
|
||||||
## 2.3 `Yonexus.Client`
|
Contains:
|
||||||
|
- `protocol/` — submodule pointing to `Yonexus.Protocol`
|
||||||
|
- `PLAN.md`
|
||||||
|
- implementation code
|
||||||
|
|
||||||
|
## 2.4 `Yonexus.Client`
|
||||||
|
|
||||||
Purpose:
|
Purpose:
|
||||||
- implementation of the client plugin
|
- implementation of the client plugin
|
||||||
@@ -53,9 +72,32 @@ Purpose:
|
|||||||
- client-side pairing confirmation and authenticated reconnect
|
- client-side pairing confirmation and authenticated reconnect
|
||||||
- client-side heartbeat and message sending
|
- client-side heartbeat and message sending
|
||||||
|
|
||||||
|
Contains:
|
||||||
|
- `protocol/` — submodule pointing to `Yonexus.Protocol`
|
||||||
|
- `PLAN.md`
|
||||||
|
- implementation code
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. System Topology
|
## 3. Repository Graph
|
||||||
|
|
||||||
|
```
|
||||||
|
Yonexus (umbrella)
|
||||||
|
├── Yonexus.Protocol (submodule)
|
||||||
|
├── Yonexus.Server (submodule)
|
||||||
|
│ └── protocol/ (nested submodule -> Yonexus.Protocol)
|
||||||
|
└── Yonexus.Client (submodule)
|
||||||
|
└── protocol/ (nested submodule -> Yonexus.Protocol)
|
||||||
|
```
|
||||||
|
|
||||||
|
Policy:
|
||||||
|
- protocol changes are always committed to `Yonexus.Protocol` first
|
||||||
|
- `Yonexus.Server` and `Yonexus.Client` update their `protocol/` submodule ref after protocol version is stable
|
||||||
|
- umbrella `Yonexus` updates its submodule refs after server/client have stable versions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. System Topology
|
||||||
|
|
||||||
A Yonexus deployment contains:
|
A Yonexus deployment contains:
|
||||||
- exactly one `Yonexus.Server` instance
|
- exactly one `Yonexus.Server` instance
|
||||||
@@ -69,7 +111,7 @@ Topology assumptions:
|
|||||||
|
|
||||||
Visual model:
|
Visual model:
|
||||||
|
|
||||||
```text
|
```
|
||||||
Yonexus.Client A --->
|
Yonexus.Client A --->
|
||||||
\
|
\
|
||||||
Yonexus.Client B ----> Yonexus.Server
|
Yonexus.Client B ----> Yonexus.Server
|
||||||
@@ -79,29 +121,24 @@ Yonexus.Client C --->
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Shared vs Split Responsibilities
|
## 5. Shared vs Split Responsibilities
|
||||||
|
|
||||||
## 4.1 Shared System Concerns
|
## 5.1 Yonexus.Protocol — Shared
|
||||||
|
|
||||||
These belong to the umbrella repo and apply to both plugins:
|
These belong to the protocol repo and apply to both plugins:
|
||||||
- protocol format
|
- protocol format and message categories
|
||||||
- builtin message types
|
- builtin message types and their semantics
|
||||||
- pairing security model
|
- pairing security model
|
||||||
- nonce/timestamp validation rules
|
- nonce/timestamp validation rules
|
||||||
- heartbeat timing rules
|
- heartbeat timing rules
|
||||||
- message rewrite rules
|
- message rewrite rules
|
||||||
- reserved rule namespace (`builtin`)
|
- reserved rule namespace (`builtin`)
|
||||||
|
- canonical JSON shapes
|
||||||
- naming and terminology
|
- naming and terminology
|
||||||
|
|
||||||
These should live primarily in:
|
## 5.2 Server-Only Concerns (Yonexus.Server)
|
||||||
- `PLAN.md`
|
|
||||||
- `PROTOCOL.md`
|
|
||||||
- `FEAT.md`
|
|
||||||
- `ARCHITECTURE.md`
|
|
||||||
|
|
||||||
## 4.2 Server-Only Concerns
|
These belong in `Yonexus.Server`:
|
||||||
|
|
||||||
These belong in `Yonexus.Server` planning/implementation:
|
|
||||||
- WebSocket server startup
|
- WebSocket server startup
|
||||||
- listen host/port config
|
- listen host/port config
|
||||||
- client registry persistence
|
- client registry persistence
|
||||||
@@ -113,9 +150,9 @@ These belong in `Yonexus.Server` planning/implementation:
|
|||||||
- client message rewriting and dispatch on server side
|
- client message rewriting and dispatch on server side
|
||||||
- sending messages to connected clients
|
- sending messages to connected clients
|
||||||
|
|
||||||
## 4.3 Client-Only Concerns
|
## 5.3 Client-Only Concerns (Yonexus.Client)
|
||||||
|
|
||||||
These belong in `Yonexus.Client` planning/implementation:
|
These belong in `Yonexus.Client`:
|
||||||
- WebSocket client connection management
|
- WebSocket client connection management
|
||||||
- reconnect/backoff logic
|
- reconnect/backoff logic
|
||||||
- local keypair generation
|
- local keypair generation
|
||||||
@@ -128,16 +165,16 @@ These belong in `Yonexus.Client` planning/implementation:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Communication Model
|
## 6. Communication Model
|
||||||
|
|
||||||
## 5.1 Transport
|
## 6.1 Transport
|
||||||
|
|
||||||
Transport is WebSocket.
|
Transport is WebSocket.
|
||||||
|
|
||||||
- `Yonexus.Server` acts as server
|
- `Yonexus.Server` acts as server
|
||||||
- `Yonexus.Client` acts as client
|
- `Yonexus.Client` acts as client
|
||||||
|
|
||||||
## 5.2 Message Categories
|
## 6.2 Message Categories
|
||||||
|
|
||||||
Two message categories exist on the same transport:
|
Two message categories exist on the same transport:
|
||||||
|
|
||||||
@@ -175,9 +212,9 @@ ${rule_identifier}::${sender_identifier}::${message_content}
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Security Architecture
|
## 7. Security Architecture
|
||||||
|
|
||||||
## 6.1 Pairing Model
|
## 7.1 Pairing Model
|
||||||
|
|
||||||
Pairing is intentionally out-of-band.
|
Pairing is intentionally out-of-band.
|
||||||
|
|
||||||
@@ -190,7 +227,7 @@ When a new client needs pairing:
|
|||||||
|
|
||||||
This preserves a basic human-mediated trust step.
|
This preserves a basic human-mediated trust step.
|
||||||
|
|
||||||
## 6.2 Post-Pairing Authentication
|
## 7.2 Post-Pairing Authentication
|
||||||
|
|
||||||
After pairing:
|
After pairing:
|
||||||
- server issues a shared secret
|
- server issues a shared secret
|
||||||
@@ -201,7 +238,7 @@ After pairing:
|
|||||||
- nonce
|
- nonce
|
||||||
- timestamp
|
- timestamp
|
||||||
|
|
||||||
## 6.3 Replay Protection
|
## 7.3 Replay Protection
|
||||||
|
|
||||||
Server enforces:
|
Server enforces:
|
||||||
- timestamp freshness (`< 10s` drift)
|
- timestamp freshness (`< 10s` drift)
|
||||||
@@ -211,9 +248,9 @@ Server enforces:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. State Ownership
|
## 8. State Ownership
|
||||||
|
|
||||||
## 7.1 Server-Owned State
|
## 8.1 Server-Owned State
|
||||||
|
|
||||||
Canonical server-owned state includes:
|
Canonical server-owned state includes:
|
||||||
- allowed client identifiers
|
- allowed client identifiers
|
||||||
@@ -226,7 +263,7 @@ Canonical server-owned state includes:
|
|||||||
- recent handshake attempt window
|
- recent handshake attempt window
|
||||||
- client liveness state
|
- client liveness state
|
||||||
|
|
||||||
## 7.2 Client-Owned State
|
## 8.2 Client-Owned State
|
||||||
|
|
||||||
Canonical client-owned state includes:
|
Canonical client-owned state includes:
|
||||||
- client identifier
|
- client identifier
|
||||||
@@ -237,83 +274,78 @@ Canonical client-owned state includes:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Plugin API Boundaries
|
## 9. Plugin API Boundaries
|
||||||
|
|
||||||
## 8.1 Yonexus.Server API
|
## 9.1 Yonexus.Server API
|
||||||
|
|
||||||
Planned public API:
|
Planned public API:
|
||||||
- `sendMessageToClient(identifier, message)`
|
- `sendMessageToClient(identifier, message)`
|
||||||
- `registerRule(rule, processor)`
|
- `registerRule(rule, processor)`
|
||||||
|
|
||||||
## 8.2 Yonexus.Client API
|
## 9.2 Yonexus.Client API
|
||||||
|
|
||||||
Planned public API:
|
Planned public API:
|
||||||
- `sendMessageToServer(message)`
|
- `sendMessageToServer(message)`
|
||||||
- `registerRule(rule, processor)`
|
- `registerRule(rule, processor)`
|
||||||
|
|
||||||
The umbrella repo should define the semantics of these APIs, but implementation details belong in each submodule.
|
The protocol defines semantics; implementation details belong in each submodule.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Documentation Ownership
|
## 10. Documentation Ownership
|
||||||
|
|
||||||
## 9.1 Umbrella Repo Docs
|
## 10.1 Umbrella Repo Docs
|
||||||
|
|
||||||
Should contain:
|
Should contain:
|
||||||
- system architecture
|
- system architecture
|
||||||
- protocol spec
|
|
||||||
- cross-cutting feature list
|
- cross-cutting feature list
|
||||||
- global design rationale
|
- global design rationale
|
||||||
|
- cross-repo coordination notes
|
||||||
|
|
||||||
## 9.2 Server Repo Docs
|
## 10.2 Protocol Repo Docs
|
||||||
|
|
||||||
|
Must contain:
|
||||||
|
- protocol specification (PROTOCOL.md)
|
||||||
|
- canonical message shapes
|
||||||
|
- protocol versioning notes
|
||||||
|
|
||||||
|
## 10.3 Server Repo Docs
|
||||||
|
|
||||||
Should contain:
|
Should contain:
|
||||||
- server setup
|
- server setup
|
||||||
- server config reference
|
- server config reference
|
||||||
- server persistence model
|
- server persistence model
|
||||||
- server operational behavior
|
- server operational behavior
|
||||||
- server implementation tasks
|
- implementation tasks
|
||||||
|
|
||||||
## 9.3 Client Repo Docs
|
## 10.4 Client Repo Docs
|
||||||
|
|
||||||
Should contain:
|
Should contain:
|
||||||
- client setup
|
- client setup
|
||||||
- client config reference
|
- client config reference
|
||||||
- client local storage model
|
- client local storage model
|
||||||
- client reconnect/heartbeat behavior
|
- client reconnect/heartbeat behavior
|
||||||
- client implementation tasks
|
- implementation tasks
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Development Flow
|
## 11. Development Flow
|
||||||
|
|
||||||
Recommended flow:
|
Recommended flow:
|
||||||
1. define cross-cutting behavior in `Yonexus`
|
1. define cross-cutting behavior in `Yonexus` umbrella
|
||||||
2. split actionable work into `Yonexus.Server` and `Yonexus.Client`
|
2. finalize protocol in `Yonexus.Protocol`
|
||||||
3. implement server-side protocol handling in `Yonexus.Server`
|
3. update submodule refs in `Yonexus.Server` and `Yonexus.Client`
|
||||||
4. implement client-side protocol handling in `Yonexus.Client`
|
4. implement server-side protocol handling in `Yonexus.Server`
|
||||||
5. keep protocol changes synchronized back into umbrella docs
|
5. implement client-side protocol handling in `Yonexus.Client`
|
||||||
|
6. keep protocol changes synchronized back into umbrella docs
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Immediate Next Documents
|
|
||||||
|
|
||||||
After this architecture file, the next documents to maintain are:
|
|
||||||
- `Yonexus.Server/PLAN.md`
|
|
||||||
- `Yonexus.Client/PLAN.md`
|
|
||||||
- optionally later:
|
|
||||||
- `Yonexus.Server/README.md`
|
|
||||||
- `Yonexus.Client/README.md`
|
|
||||||
- server/client task breakdown docs
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 12. Non-Goals of the Umbrella Repo
|
## 12. Non-Goals of the Umbrella Repo
|
||||||
|
|
||||||
The umbrella repo should avoid becoming:
|
The umbrella repo should avoid becoming:
|
||||||
- the only place where implementation changes happen
|
- the place where all implementation code lives
|
||||||
- a dumping ground for server-only details
|
- a dumping ground for server-only or client-only details
|
||||||
- a dumping ground for client-only details
|
|
||||||
- a duplicate of submodule READMEs without system-level value
|
- a duplicate of submodule READMEs without system-level value
|
||||||
|
|
||||||
Its job is coordination, not code concentration.
|
Its job is coordination, not code concentration.
|
||||||
|
|||||||
85
README.md
85
README.md
@@ -1,11 +1,13 @@
|
|||||||
# Yonexus
|
# Yonexus
|
||||||
|
|
||||||
Yonexus is a cross-instance communication system for OpenClaw built as **two separate plugins**:
|
Yonexus is a cross-instance communication system for OpenClaw built as **three separate repositories**:
|
||||||
|
|
||||||
- `Yonexus.Server`
|
| Repository | Role |
|
||||||
- `Yonexus.Client`
|
|---|---|
|
||||||
|
| `Yonexus` | Umbrella — architecture, planning, and coordination |
|
||||||
It is designed for scenarios where one OpenClaw instance acts as a central communication hub and multiple other OpenClaw instances connect to it as clients.
|
| `Yonexus.Server` | Central hub plugin — accepts client connections, handles pairing/authentication |
|
||||||
|
| `Yonexus.Client` | Client plugin — connects to server, manages local identity |
|
||||||
|
| `Yonexus.Protocol` | Shared protocol specification — referenced as a submodule by both Server and Client |
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -33,6 +35,22 @@ Responsibilities:
|
|||||||
- send messages to server
|
- send messages to server
|
||||||
- receive messages from server via rule dispatch
|
- receive messages from server via rule dispatch
|
||||||
|
|
||||||
|
### Yonexus.Protocol
|
||||||
|
Shared protocol specification repository. Both `Yonexus.Server` and `Yonexus.Client` reference this as a submodule at `protocol/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Yonexus (umbrella)
|
||||||
|
├── Yonexus.Protocol ← shared protocol submodule
|
||||||
|
├── Yonexus.Server ← server plugin submodule
|
||||||
|
│ └── protocol/ ← points to Yonexus.Protocol
|
||||||
|
└── Yonexus.Client ← client plugin submodule
|
||||||
|
└── protocol/ ← points to Yonexus.Protocol
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
@@ -67,48 +85,46 @@ After pairing:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Current Spec Files
|
## Current Repository Spec Files
|
||||||
|
|
||||||
This repository currently contains planning/spec-first documents:
|
|
||||||
|
|
||||||
|
### Umbrella (`Yonexus`)
|
||||||
- `PLAN.md` — project plan and architecture
|
- `PLAN.md` — project plan and architecture
|
||||||
- `PROTOCOL.md` — builtin WebSocket protocol details
|
- `ARCHITECTURE.md` — architecture overview and repository graph
|
||||||
- `FEAT.md` — implementation feature checklist
|
- `FEAT.md` — implementation feature checklist
|
||||||
|
|
||||||
---
|
### Protocol (`Yonexus.Protocol`)
|
||||||
|
- `PROTOCOL.md` — shared communication protocol specification
|
||||||
|
|
||||||
## Planned Plugin Manifests
|
### Server (`Yonexus.Server`)
|
||||||
|
- `PLAN.md` — server-specific implementation plan
|
||||||
|
- `protocol/` — submodule pointing to `Yonexus.Protocol`
|
||||||
|
|
||||||
This repo is expected to define separate plugin manifests for:
|
### Client (`Yonexus.Client`)
|
||||||
|
- `PLAN.md` — client-specific implementation plan
|
||||||
- `plugin.server.json`
|
- `protocol/` — submodule pointing to `Yonexus.Protocol`
|
||||||
- `plugin.client.json`
|
|
||||||
|
|
||||||
These represent the initial config surface for `Yonexus.Server` and `Yonexus.Client`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Planned TypeScript APIs
|
## Planned TypeScript APIs
|
||||||
|
|
||||||
### Yonexus.Server
|
### Yonexus.Server
|
||||||
- `sendMessageToClient(identifier: string, message: string)`
|
```ts
|
||||||
- `registerRule(rule: string, processor: (message: string) => unknown)`
|
sendMessageToClient(identifier: string, message: string): Promise<void>
|
||||||
|
registerRule(rule: string, processor: (message: string) => unknown): void
|
||||||
|
```
|
||||||
|
|
||||||
### Yonexus.Client
|
### Yonexus.Client
|
||||||
- `sendMessageToServer(message: string)`
|
```ts
|
||||||
- `registerRule(rule: string, processor: (message: string) => unknown)`
|
sendMessageToServer(message: string): Promise<void>
|
||||||
|
registerRule(rule: string, processor: (message: string) => unknown): void
|
||||||
|
```
|
||||||
|
|
||||||
Message format:
|
Message format:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
${rule_identifier}::${message_content}
|
${rule_identifier}::${message_content}
|
||||||
```
|
```
|
||||||
|
|
||||||
Reserved rule:
|
Reserved rule: `builtin`
|
||||||
|
|
||||||
```text
|
|
||||||
builtin
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -140,19 +156,16 @@ builtin
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Current repository status:
|
|
||||||
- planning/specification stage
|
- planning/specification stage
|
||||||
- split-plugin architecture defined
|
- split-plugin architecture defined
|
||||||
- protocol draft defined
|
- protocol draft defined in `Yonexus.Protocol`
|
||||||
- implementation not started yet
|
- implementation not started yet
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Next Steps
|
## Repository URLs
|
||||||
|
|
||||||
Recommended next deliverables:
|
- [Yonexus (umbrella)](https://git.hangman-lab.top/nav/Yonexus)
|
||||||
- plugin manifests
|
- [Yonexus.Server](https://git.hangman-lab.top/nav/Yonexus.Server)
|
||||||
- source tree scaffolds for server/client
|
- [Yonexus.Client](https://git.hangman-lab.top/nav/Yonexus.Client)
|
||||||
- protocol model types
|
- [Yonexus.Protocol](https://git.hangman-lab.top/nav/Yonexus.Protocol)
|
||||||
- transport skeleton
|
|
||||||
- pairing/auth flow implementation
|
|
||||||
|
|||||||
Submodule Yonexus.Client updated: c5330bb9f9...183f2eea6d
1
Yonexus.Protocol
Submodule
1
Yonexus.Protocol
Submodule
Submodule Yonexus.Protocol added at 9232aa7c17
Submodule Yonexus.Server updated: 23969afa80...871fe94318
Reference in New Issue
Block a user