Skip to main content

Manifest Reference

Complete reference for the AppManifest schema used to declare application capabilities, dependencies, permissions, and configuration.

Overview

The manifest is a protobuf-based schema that apps provide during registration via the onRegister lifecycle hook. It declares:

  • Identity: App name and authentication certificate
  • Frontend: UI mode, assets, and navigation structure
  • Backend: API routes, proxying rules, and permissions
  • Dependencies: Required apps (HARD/SOFT) with version constraints
  • Settings: Configurable parameters with validation and encryption
  • Permissions: Required capabilities and exposed permissions

AppManifest (Root)

Based on easy.proto/v2/protos/manifest.proto:

interface AppManifest {
name: string // Unique app identifier (e.g., "todos", "crm")
certificate: Uint8Array // X.509 certificate for verification
web_app: WebApp // Frontend + API configuration
required_permissions: Permission[] // Requested capabilities
dependencies: DependencyRequirement[] // App dependencies
settings: SettingsSchema // Configuration schema
}

Example

{
name: "todos-app",
certificate: certificateBytes,
web_app: {
assets: [...],
ui: {...},
api: {...}
},
required_permissions: [...],
dependencies: [...],
settings: {...}
}

WebApp Structure

Combines frontend and backend contracts:

interface WebApp {
assets: Asset[] // Static files (JS, CSS, HTML, images, fonts)
ui?: UI // UI rendering contract (optional for API-only apps)
api: WebApi // API proxy contract (optional for UI-only apps)
}

Requirements:

  • At least one of ui or api must be provided
  • Assets must include entry point referenced in ui.entry_asset_name

Assets

Static files served by the UI engine:

interface Asset {
name: string // Filename (e.g., "remoteEntry.js", "styles.css")
mime_type: string // Content type (e.g., "application/javascript")
contents: Uint8Array // Raw file contents
signature: Uint8Array // Detached signature for verification
sha256: string // Hex-encoded SHA-256 hash
}

Supported MIME Types

JavaScript:

  • application/javascript
  • text/javascript

Stylesheets:

  • text/css

Markup:

  • text/html

Data:

  • application/json

Images:

  • image/png, image/jpeg, image/svg+xml, image/webp

Fonts:

  • font/woff2, font/woff

Example

{
name: "remoteEntry.js",
mime_type: "application/javascript",
contents: fileBuffer,
signature: signatureBytes,
sha256: "a3b2c1d4e5f6..."
}

UI Configuration

Based on easy.proto/v2/protos/ui.proto:

interface UI {
mode: UIMode // Loading mechanism
entry_asset_name: string // Entry point filename
exposed_module?: string // Module export (not for WEB_COMPONENT)
route_base: string // Mount path (e.g., "/apps/todos")
ssr: boolean // Server-side rendering support
navigation: NavigationRoute[] // Menu structure (max 3 levels)
}

enum UIMode {
FEDERATION // Webpack Module Federation (recommended)
WEB_COMPONENT // Custom HTML element
ESM_COMPONENT // ES module export
}

UI Modes

FEDERATION (Recommended):

{
mode: UIMode.FEDERATION,
entry_asset_name: "remoteEntry.js",
exposed_module: "./App",
route_base: "/apps/todos",
ssr: false
}

WEB_COMPONENT:

{
mode: UIMode.WEB_COMPONENT,
entry_asset_name: "component.js",
// exposed_module not used
route_base: "/apps/todos"
}

ESM_COMPONENT:

{
mode: UIMode.ESM_COMPONENT,
entry_asset_name: "main.js",
exposed_module: "TodosApp",
route_base: "/apps/todos"
}

3-level hierarchical navigation (Rail → Drawer → Collapsible):

interface NavigationRoute {
title: string // Display name (max 100 chars)
href: string // Route path (supports :params)
icon?: string // SVG string, URL, or data URI
required_permissions: string[] // Access control (empty = public)
children: NavigationRoute[] // Sub-routes (max 3 levels total)
metadata: Record<string, string> // Extensibility
}
{
title: "Projects",
href: "/apps/todos/projects",
icon: "<svg>...</svg>",
required_permissions: ["todos:projects:read"],
children: [
{
title: "Active",
href: "/apps/todos/projects/active",
required_permissions: ["todos:projects:read"],
children: [
{
title: "High Priority",
href: "/apps/todos/projects/active/priority",
required_permissions: ["todos:projects:read"]
}
]
},
{
title: "Archived",
href: "/apps/todos/projects/archived",
required_permissions: ["todos:projects:read"]
}
]
}
  • Max Depth: 3 levels (Rail → Drawer → Collapsible)
  • Title Length: Max 100 characters
  • Path Format: Must start with /, should start with app's route_base
  • Icon Format: SVG string, URL, or data URI
  • Permissions: Empty array = public route

API Configuration

Based on easy.proto/v2/protos/api.proto:

interface WebApi {
base_path: string // Public proxy path (e.g., "/api/apps/todos")
upstream_base_url: string // Internal backend URL (e.g., "http://svc:8080")
strip_base_path: boolean // Remove base_path before forwarding (default: true)
timeout_ms: number // Request timeout in milliseconds
forward_headers: string[] // Header allowlist (lowercase)
routes: RouteSpec[] // Fine-grained routing rules
required_permissions: string[] // Access scopes
default_rate_limit: RateLimit // Global rate limits
health_check: HealthCheck // Upstream health monitoring
openapi_url?: string // API documentation URL
}

API Example

{
base_path: "/api/apps/todos",
upstream_base_url: "http://todos-bff.svc:8080",
strip_base_path: true,
timeout_ms: 30000,
forward_headers: ["authorization", "x-request-id", "content-type"],
routes: [
{
pattern: "/items/**",
methods: ["GET", "POST", "PUT", "DELETE"],
scopes: ["todos:items:read", "todos:items:write"],
timeout_ms: 5000,
rate_limit: { rpm: 100, burst: 20 }
},
{
pattern: "/public/**",
methods: ["GET"],
is_public: true // No authentication required
}
],
required_permissions: ["todos:api:access"],
default_rate_limit: { rpm: 60, burst: 10 },
health_check: {
path: "/health",
interval_seconds: 30,
timeout_ms: 5000,
unhealthy_threshold: 3
}
}

RouteSpec

Fine-grained routing rules:

interface RouteSpec {
pattern: string // Path pattern (prefix or regex)
methods: string[] // HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
scopes: string[] // Required RBAC scopes
timeout_ms?: number // Route-specific timeout (overrides default)
rate_limit?: RateLimit // Route-specific limits (overrides default)
is_public: boolean // Skip authentication (default: false)
}

Pattern Types

Prefix Matching (/**):

pattern: "/items/**"  // Matches /items, /items/1, /items/1/subtasks

Single Segment (/*):

pattern: "/items/*"   // Matches /items/1, but not /items/1/subtasks

Regex (regex:):

pattern: "regex:^/items/\\d+$"  // Matches /items/123

Exact:

pattern: "/items"  // Only matches /items exactly

Rate Limiting

Request rate limits with burst capacity:

interface RateLimit {
rpm: number // Requests per minute
burst: number // Burst capacity for spikes
}

Hierarchical Application:

  1. RouteSpec.rate_limit (highest priority)
  2. WebApi.default_rate_limit (fallback)
  3. No limit (if neither configured)

Enforcement:

  • Route-Level: Applied to all users (key: route:{routeID})
  • Per-User: Applied per authenticated user (key: {subject}:route:{routeID})

Example

{
rpm: 100, // Allow 100 requests per minute
burst: 20 // Allow bursts up to 20 additional requests
}

Health Checks

Upstream service health monitoring:

interface HealthCheck {
path: string // Health endpoint (e.g., "/health")
interval_seconds: number // Check interval (default: 30)
timeout_ms: number // Check timeout (default: 5000)
unhealthy_threshold: number // Failures before unhealthy (default: 3)
}

Example

{
path: "/health",
interval_seconds: 30,
timeout_ms: 5000,
unhealthy_threshold: 3
}

Permissions

Resource-based access control:

interface Permission {
scope: string // Permission scope (e.g., "read_campaigns_any")
resource_type: string // Target resource (e.g., "campaigns")
reason: string // Request justification
requires_approval: boolean // Admin approval required (default: false)
}

Example

{
scope: "users:read",
resource_type: "users",
reason: "Access user data for profile display",
requires_approval: false
}

Dependencies

App dependencies with version constraints:

interface DependencyRequirement {
app_name: string // Dependent app identifier
kind: DependencyKind // HARD or SOFT
version_constraint?: string // Semantic versioning constraint
}

enum DependencyKind {
HARD // MUST be installed (blocks installation)
SOFT // CAN be installed (provides warnings)
}

Version Constraints

Exact Version:

version_constraint: "1.2.3"

Minimum Version:

version_constraint: ">=1.0.0"

Version Range:

version_constraint: ">=1.0.0 <2.0.0"

Patch Updates:

version_constraint: "~1.2.0"  // 1.2.x

Minor + Patch Updates:

version_constraint: "^1.2.0"  // 1.x.y

Dependency Example

{
app_name: "auth-service",
kind: DependencyKind.HARD,
version_constraint: ">=2.0.0 <3.0.0"
}

Settings Schema

Configurable parameters with validation:

interface SettingsSchema {
definitions: SettingDefinition[]
}

interface SettingDefinition {
key: string // Setting identifier (unique per app)
data_type: SettingDataType // Value type
required: boolean // Mandatory flag (default: false)
default_value?: string // JSON-encoded default
validation: ValidationRules // Constraints
description: string // User-facing help
sensitive: boolean // Encryption flag (default: false)
ui_hints: UIHints // Form generation hints
}

Data Types

enum SettingDataType {
STRING // Optional string
STRING_REQUIRED // Required string
INT // Optional integer
INT_REQUIRED // Required integer
FLOAT // Optional float
FLOAT_REQUIRED // Required float
BOOL // Optional boolean
BOOL_REQUIRED // Required boolean
STRING_ARRAY // Array of strings
INT_ARRAY // Array of integers
FLOAT_ARRAY // Array of floats
BOOL_ARRAY // Array of booleans
JSON // Optional JSON object
JSON_REQUIRED // Required JSON object
KVPAIR_ARRAY // Array of {key, value} pairs
}

Validation Rules

interface ValidationRules {
min_length?: number // String minimum length
max_length?: number // String maximum length
min?: number // Numeric minimum value
max?: number // Numeric maximum value
pattern?: string // Regex pattern
enum?: string[] // Allowed values
}

UI Hints

interface UIHints {
input_type?: string // Form control type
placeholder?: string // Field placeholder
help_text?: string // Field help text
multiline?: boolean // Textarea mode
step?: number // Numeric step
options?: string[] // Select options
}

Input Types:

  • text - Single-line text input
  • password - Password field (masked)
  • textarea - Multi-line text
  • select - Dropdown select
  • number - Numeric input
  • checkbox - Boolean checkbox
  • radio - Radio button group

Settings Example

{
definitions: [
{
key: "api_key",
data_type: SettingDataType.STRING_REQUIRED,
required: true,
validation: {
min_length: 8,
max_length: 64,
pattern: "^[A-Za-z0-9_-]+$"
},
description: "API key for external service",
sensitive: true,
ui_hints: {
input_type: "password",
placeholder: "Enter your API key",
help_text: "Contact support to obtain an API key"
}
},
{
key: "max_connections",
data_type: SettingDataType.INT,
default_value: "10",
validation: {
min: 1,
max: 100
},
description: "Maximum database connections",
ui_hints: {
input_type: "number",
step: 1
}
},
{
key: "environment",
data_type: SettingDataType.STRING_REQUIRED,
required: true,
validation: {
enum: ["development", "staging", "production"]
},
description: "Deployment environment",
ui_hints: {
input_type: "select",
options: ["development", "staging", "production"]
}
}
]
}

Complete Manifest Example

const manifest: AppManifest = {
name: "todos-app",
certificate: certificateBytes,

web_app: {
assets: [
{
name: "remoteEntry.js",
mime_type: "application/javascript",
contents: remoteEntryBuffer,
signature: signatureBytes,
sha256: "a3b2c1..."
},
{
name: "styles.css",
mime_type: "text/css",
contents: stylesBuffer,
signature: signatureBytes,
sha256: "d4e5f6..."
}
],

ui: {
mode: UIMode.FEDERATION,
entry_asset_name: "remoteEntry.js",
exposed_module: "./App",
route_base: "/apps/todos",
ssr: false,
navigation: [
{
title: "Projects",
href: "/apps/todos/projects",
icon: "<svg>...</svg>",
required_permissions: ["todos:projects:read"],
children: [
{
title: "Active",
href: "/apps/todos/projects/active",
required_permissions: ["todos:projects:read"]
}
]
}
]
},

api: {
base_path: "/api/apps/todos",
upstream_base_url: "http://todos-bff.svc:8080",
strip_base_path: true,
timeout_ms: 30000,
forward_headers: ["authorization", "x-request-id"],
routes: [
{
pattern: "/items/**",
methods: ["GET", "POST", "PUT", "DELETE"],
scopes: ["todos:items:read", "todos:items:write"],
timeout_ms: 5000,
rate_limit: { rpm: 100, burst: 20 }
}
],
required_permissions: ["todos:api:access"],
default_rate_limit: { rpm: 60, burst: 10 },
health_check: {
path: "/health",
interval_seconds: 30,
timeout_ms: 5000,
unhealthy_threshold: 3
}
}
},

required_permissions: [
{
scope: "users:read",
resource_type: "users",
reason: "Access user data for task assignments",
requires_approval: false
}
],

dependencies: [
{
app_name: "auth-service",
kind: DependencyKind.HARD,
version_constraint: ">=2.0.0 <3.0.0"
},
{
app_name: "analytics",
kind: DependencyKind.SOFT,
version_constraint: ">=1.0.0"
}
],

settings: {
definitions: [
{
key: "database_url",
data_type: SettingDataType.STRING_REQUIRED,
required: true,
description: "PostgreSQL connection URL",
sensitive: true,
ui_hints: {
input_type: "password",
placeholder: "postgresql://user:pass@host:5432/db"
}
}
]
}
}

Validation Rules

Manifest-Level

  • name: Must be unique across all apps
  • certificate: Valid X.509 certificate required
  • web_app: At least one of ui or api must be provided

UI-Level

  • entry_asset_name: Must reference an asset in web_app.assets
  • route_base: Must start with /
  • navigation: Max 3 levels deep
  • navigation.title: Max 100 characters
  • navigation.href: Must start with /

API-Level

  • base_path: Must start with /
  • upstream_base_url: Valid URL required
  • timeout_ms: Must be > 0
  • routes.pattern: Valid regex if using regex: prefix
  • routes.methods: At least one method required

Settings-Level

  • key: Must be unique within app
  • data_type: Must be valid SettingDataType
  • default_value: Must validate against rules
  • validation.pattern: Must be valid regex if provided
  • validation.min/max: Min must be less than or equal to max if both provided

Proto File Locations

  • Main Manifest: easy.proto/v2/protos/manifest.proto
  • UI Configuration: easy.proto/v2/protos/ui.proto
  • API Configuration: easy.proto/v2/protos/api.proto
  • Common Types: easy.proto/v2/protos/common.proto