OutLayer Documentation
Projects
Projects allow you to organize your WASM code with version management, persistent storage, and project-level secrets. All versions of a project share the same resources, enabling seamless updates.
What are Projects?#
A Project is a container for WASM code versions with shared resources:
- Versioning: Deploy multiple versions, switch active version anytime
- Persistent Storage: Data survives version updates (same encryption key)
- Project Secrets: Secrets accessible by all versions of the project
- Storage Deposit: Pay for on-chain storage, refunded when deleted
Why use Projects? Without projects, each new WASM hash gets its own storage namespace. With projects, you can update your code while keeping all user data intact.
Project ID Format#
Each project has a unique ID composed of the owner account and project name:
{owner_account_id}/{project_name}
Examples:
alice.near/my-app
bob.testnet/weather-bot
mycompany.near/trading-oracleProject names must be alphanumeric with dashes and underscores only (pattern: [a-zA-Z0-9_-]+).
Creating a Project#
Create projects via the Projects Dashboard:
- Go to /projects
- Click "New Project"
- Enter project name
- Select code source (GitHub repo or WASM URL)
- Click "Create Project"
Important: Persistent storage requires WASI Preview 2 (wasm32-wasip2). Make sure to select this build target when creating your project. WASI P1 does not support storage.
Project Binding in WASM Code#
For storage to work correctly, your WASM code must declare which project it belongs to using the metadata! macro from the OutLayer SDK:
use outlayer::{metadata, storage};
// REQUIRED for project-based execution and storage
// project must match your project_id on the OutLayer contract!
metadata! {
project: "alice.near/my-app", // Format: {owner_account_id}/{project_name}
version: "1.0.0", // Optional: for tracking
}Critical: The project field must exactly match your project ID on the contract (e.g., alice.near/my-app). If they don't match, storage operations will fail or use the wrong namespace.
Why is this needed?
- Storage namespace: The project ID determines which storage namespace your code uses
- Encryption key: Storage encryption keys are derived from
storage:{project_uuid} - Version continuity: All versions of the same project share storage because they have the same project ID
- Secrets access: Project secrets are only accessible to WASM with matching project ID
What happens without metadata?
- Storage calls will fail with "project not found" error
- Secrets won't be decrypted (project binding validation fails)
- Each WASM hash gets isolated storage instead of shared project storage
Tip: Copy the exact project ID from the Projects dashboard to avoid typos. The format is always {owner_account_id}/{project_name}.
Managing Versions#
Each project can have multiple versions. The active version is used for new executions by default.
Adding a Version#
In the Projects dashboard, expand your project and click "Add Version". You can choose to make it active immediately or keep the current active version.
Switching Active Version#
Click the checkmark icon on any version to make it active. The currently active version cannot be removed.
Note: You cannot remove the active version. First switch to another version, then remove.
Executing a Specific Version#
By default, executions use the active version. But you can execute any specific version using the version_key parameter. This is useful for testing new versions before making them active.
// Contract call: request_execution
{
"code_source": {
"Project": {
"project_id": "alice.near/my-app",
"version_key": "zavodil/[email protected]" // Optional: specific version
}
},
"input_data": "...",
"resource_limits": { ... }
}
// If version_key is omitted, the active version is usedUse Case: Deploy a new version, test it by specifying its version_key, and only set it as active once you confirm it works correctly. Both versions share the same storage.
Persistent Storage#
All versions of a project share the same storage namespace. Data written by v1 is readable by v2. Storage is encrypted using the keystore TEE and isolated per user.
Requires WASI Preview 2: Storage host functions are only available in WASI P2 components (wasm32-wasip2). WASI P1 modules do not have access to persistent storage.
Encrypted
All data encrypted with project-specific keys in TEE
User-Isolated
Each user has their own namespace, automatic isolation
Atomic Operations
Increment, decrement, compare-and-swap for concurrency
For detailed information about storage API, methods, atomic operations, and usage examples, see the dedicated documentation:
Storage Documentation →Project Secrets#
Secrets can be bound to a project, making them accessible by all versions. See Secrets Documentation for details on creating and managing project secrets.
Benefit: When you update your code to a new version, project secrets remain accessible. No need to re-create or migrate secrets.
Use Cases#
Hot Updates
Deploy a new version, test it via version_key, then switch active version. Rollback instantly if needed.
Data Migration
New version reads old data format using get_by_version, transforms it, writes new format.
Shared Secrets
API keys and credentials available to all versions. No need to re-enter secrets for each update.
User State Persistence
Store user preferences, counters, session data that persist across executions and version updates.
Caching
Cache expensive computation results. Subsequent executions read from cache instead of recomputing.
Worker-Private State
Use set_worker/get_worker for internal WASM state that users cannot access directly.
Atomic Counters & CAS
Use increment/decrement for concurrent-safe counters, or set_if_equals for compare-and-swap operations.
Best Practices#
- Use WASI Preview 2 (wasm32-wasip2) build target for storage support
- Use descriptive project names that reflect functionality
- Tag your git commits (e.g.,
v1.0.0) before adding versions - Test new versions via version_key before setting as active
- Keep at least one known-good version as fallback
- Use project secrets for shared credentials instead of repo-based secrets
- Document your storage key format for data migrations
- Use key prefixes (e.g.,
user:alice:) for organization - Use
increment/decrementfor counters instead of get+set (race-safe) - Use
set_if_absentfor one-time initialization to avoid overwriting
Related Documentation
- Persistent Storage - Storage API, methods, atomic operations
- Project Secrets - Binding secrets to projects
- Building OutLayer App - WASI P1 vs P2, building WASM modules
- Pricing & Limits - Understanding storage costs