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-oracle

Project names must be alphanumeric with dashes and underscores only (pattern: [a-zA-Z0-9_-]+).

Creating a Project#

Create projects via the Projects Dashboard:

  1. Go to /projects
  2. Click "New Project"
  3. Enter project name
  4. Select code source (GitHub repo or WASM URL)
  5. 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 used

Use 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/decrement for counters instead of get+set (race-safe)
  • Use set_if_absent for one-time initialization to avoid overwriting

Related Documentation