User ID Management
Learn how to manage user identity and maintain conversation persistence across sessions using the Estuary SDK.
Overview
User ID management is crucial for:
- Conversation Persistence: Continue conversations where the user left off
- Context Retention: AI remembers previous interactions
- User Personalization: Tailor experiences based on history
The Estuary SDK provides automatic User ID management through the EstuaryCredentials component.
How User IDs Work
Each user is identified by a unique userId (also called playerId in the SDK). This ID is sent with every connection to the Estuary server.
┌─────────────────────────────────────────────────────────────┐
│ User ID Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ Session 1 (First Use) │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ EstuaryCredentials │---→│ Generate User ID │ │
│ │ (userId empty) │ │ "spectacles_abc123"│ │
│ └────────────────────┘ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Store in Device │ │
│ │ PersistentStorage │ │
│ └───────────────────┘ │
│ │
│ Session 2+ (Return User) │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ EstuaryCredentials │---→│ Load from Storage │ │
│ │ (userId empty) │ │ "spectacles_abc123"│ │
│ └────────────────────┘ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Same User ID! │ │
│ │ Conversation │ │
│ │ Context Restored │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Automatic User ID Management
The EstuaryCredentials component handles User IDs automatically:
Priority Order
- Manual ID (if
userIdFieldis set in Inspector) → Used and stored - Stored ID (from device's PersistentStorage) → Loaded and used
- Generated ID (if no stored ID exists) → Created and stored
Default Behavior
// When EstuaryCredentials initializes, it:
// 1. Checks if userIdField has a value
if (this.userIdField && this.userIdField.length > 0) {
this._resolvedUserId = this.userIdField;
store.putString("estuary_user_id", this._resolvedUserId);
// Manual ID will be used and persisted
}
// 2. Otherwise, tries to load from storage
else if (store.has("estuary_user_id")) {
this._resolvedUserId = store.getString("estuary_user_id");
// Previous session's ID restored
}
// 3. Otherwise, generates a new ID
else {
this._resolvedUserId = "spectacles_" + Date.now().toString(36);
store.putString("estuary_user_id", this._resolvedUserId);
// New user, new ID
}
Configuration Options
Using Inspector (Recommended)
In the EstuaryCredentials component Inspector:
| Field | Description |
|---|---|
| User ID | Leave empty for automatic management, or enter a specific ID |
Console Output
When initialized, EstuaryCredentials logs the User ID:
╔════════════════════════════════════════════════════════════╗
║ ESTUARY USER ID: spectacles_lq8x7k4j ║
╚════════════════════════════════════════════════════════════╝
Manual User ID Setting
For Testing Specific Conversations
You can set a specific User ID to test or resume a specific conversation:
- Open
EstuaryCredentialsin the Inspector - Enter the desired User ID in the
User IDfield - This ID will be used and stored for future sessions
// In Inspector:
userIdField: "test_user_12345"
// Result:
// - Uses "test_user_12345" for this and future sessions
// - Previous conversation with this ID will be restored
Programmatic User ID
For dynamic User ID assignment:
@component
export class CustomUserManagement extends BaseScriptComponent {
onAwake() {
// Create character with specific user ID
const userId = this.getOrCreateUserId();
const character = new EstuaryCharacter(characterId, userId);
}
private getOrCreateUserId(): string {
const store = global.persistentStorageSystem.store;
// Check for existing ID
if (store.has("my_custom_user_id")) {
return store.getString("my_custom_user_id");
}
// Generate new ID based on your logic
const newId = this.generateCustomId();
store.putString("my_custom_user_id", newId);
return newId;
}
private generateCustomId(): string {
// Your custom ID generation logic
return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
Accessing the User ID
From EstuaryCredentials Singleton
import { EstuaryCredentials } from 'estuary-lens-studio-sdk';
// Check if credentials exist
if (EstuaryCredentials.hasInstance) {
const userId = EstuaryCredentials.instance.userId;
print(`Current User ID: ${userId}`);
}
From Session Info
After connecting, the session info includes the player ID:
character.on('connected', (session: SessionInfo) => {
print(`Session ID: ${session.sessionId}`);
print(`Player ID: ${session.playerId}`);
print(`Conversation ID: ${session.conversationId}`);
});
The conversationId is particularly useful - it remains the same across sessions for the same user, enabling conversation continuity.
Conversation Persistence
How It Works
When a user reconnects with the same User ID:
- Server recognizes the
playerId - Loads previous conversation history
- AI has full context of past interactions
- Conversation continues seamlessly
Session vs Conversation
| Concept | Description | Persistence |
|---|---|---|
| Session ID | Unique per connection | Changes each connect |
| Player ID | User identifier | Persists on device |
| Conversation ID | Thread identifier | Tied to Player ID |
Resetting User Identity
Clear Stored User ID
To start fresh (new user, new conversation):
// Clear the stored User ID
const store = global.persistentStorageSystem.store;
store.remove("estuary_user_id");
// Next initialization will generate a new ID
Force New Conversation
Alternatively, use a different User ID format:
// Generate unique ID per session (no persistence)
const sessionOnlyId = `session_${Date.now()}`;
const character = new EstuaryCharacter(characterId, sessionOnlyId);
Multi-User Scenarios
For experiences with multiple users:
@component
export class MultiUserManager extends BaseScriptComponent {
private users: Map<string, EstuaryCharacter> = new Map();
/**
* Create or get a character instance for a specific user
*/
getCharacterForUser(userId: string): EstuaryCharacter {
if (this.users.has(userId)) {
return this.users.get(userId)!;
}
const character = new EstuaryCharacter(
characterId,
userId
);
this.users.set(userId, character);
return character;
}
/**
* Switch active user
*/
switchToUser(userId: string) {
const character = this.getCharacterForUser(userId);
EstuaryManager.instance.setActiveCharacter(character);
}
}
Best Practices
Let the SDK Manage IDs
Unless you have specific requirements, let EstuaryCredentials handle User IDs automatically:
// Just leave userIdField empty in Inspector
// SDK handles generation and persistence
Don't Expose User IDs
User IDs should be treated as internal identifiers:
// Don't show to users
print(`Your ID is: ${userId}`);
// Keep internal
// Just use for conversation persistence
Handle Storage Errors Gracefully
try {
const store = global.persistentStorageSystem.store;
userId = store.getString("estuary_user_id");
} catch (e) {
// Fall back to generated ID
userId = this.generateFallbackId();
}
Consider Privacy
- User IDs should not contain personal information
- Generated IDs are random and anonymous
- No user data is stored in the ID itself
Troubleshooting
User ID Not Persisting
- Ensure
EstuaryCredentialsis in your scene - Check that it runs before other Estuary components
- Verify
PersistentStorageSystemis available
Wrong Conversation Context
If the AI doesn't remember previous conversations:
- Verify the same User ID is being used
- Check console for the User ID banner
- Try setting the ID manually in Inspector
Multiple IDs Being Generated
If a new ID is generated each session:
- Check for errors in PersistentStorage
- Ensure the storage key isn't being cleared elsewhere
- Verify
EstuaryCredentials.onAwake()is running
Next Steps
- Action System - Trigger scene actions
- API Reference - Component details
- Voice Connection - Audio implementation