Skip to main content

Deploy Applets with Configuration

When building MCP servers or other applets that need to connect to external services, you often require configuration values like API endpoints, hostnames, ports, or other settings that shouldn't be hardcoded into your applet. WeilChain provides a secure configuration system that allows you to deploy applets with custom configurations while keeping sensitive information separate from your code.

Why Use External Configuration?

Hardcoding configuration values in your applet has several drawbacks:

  • Security Concerns: Sensitive information like API keys or internal hostnames become visible in the applet code
  • Flexibility: Different deployments might need different configurations (development vs production)
  • Reusability: Other users can't easily adapt your applet for their own infrastructure
  • Maintenance: Changing configuration requires recompiling and redeploying the entire applet

WeilChain's configuration system solves these problems by allowing you to specify configuration externally at deployment time.

Defining Configuration Structure

First, define your configuration structure in your WIDL file using a record type. This creates a structured schema for your configuration values.

Example: Postgres Service Configuration

postgres.widl
record PostgresConfig {
host: string,
port: string
}

@mcp
interface Postgres {
config -> PostgresConfig;

// This returns the schema of the database with name given by argument `db_name`.
query func schema(db_name: string) -> result<string, string>;
// This runs a query provided in argument `query_str` on the database with name given by argument `db_name`.
query func run_query(db_name: string, query_str: string) -> result<list<string>, string>;
// This executes the statement provided in argument `statement` potentially mutating the rows of the database with name given by argument `db_name`.
query func execute(db_name: string, statement: string) -> result<u64, string>
}

Configuration Schema Definition

The record type defines the structure of your configuration:

  • Field Types: Use appropriate WIDL types (string for now)
  • Required Fields: All fields in a record are required by default
  • Naming: Use clear, descriptive field names that indicate their purpose

Creating Configuration Files

Create a YAML configuration file that matches your defined schema. This file contains the actual values for your deployment.

Example Configuration File

config.yaml
host: "1.2.3.4"
port: "1234"

Deploying with Configuration

Use the enhanced deploy command to include your configuration file:

deploy -f path/to/applet.wasm -p path/to/applet.widl -c path/to/config.yaml

Deploy Command Parameters

  • -f: Path to your compiled WASM applet
  • -p: Path to your WIDL interface definition
  • -c: Path to your YAML configuration file

Deployment Process

When you deploy with configuration:

  1. Validation: The deploy tool validates your configuration against the WIDL schema
  2. Encryption: Configuration values are securely encrypted before storage
  3. Registration: The applet is registered with its associated configuration
  4. Access Control: Only your applet instance can access its configuration

Implementing Configuration in Rust

Applet State Setup

Import the necessary types and set up your applet state to use the configuration system:

src/lib.rs
use serde::{Deserialize, Serialize};
use weil_macros::{constructor, mutate, query, smart_contract, WeilType};
use weil_rs::config::Secrets;

// Your configuration structure (generated from WIDL)
#[derive(Serialize, Deserialize, Clone)]
pub struct PostgresConfig {
pub host: String,
pub port: String,
}

#[derive(Serialize, Deserialize, WeilType)]
pub struct PostgresContractState {
secrets: Secrets<PostgresConfig>,
}

Constructor Implementation

Initialize your applet state with the configuration system:

#[smart_contract]
impl Postgres for PostgresContractState {
#[constructor]
fn new() -> Result<Self, String>
where
Self: Sized,
{
Ok(PostgresContractState {
secrets: Secrets::new(),
})
}

// Implementation methods...
}

Using Configuration Values

Access configuration values through the secrets field in your applet methods:

impl PostgresContractState {
fn url(&self, endpoint: &str) -> String {
format!(
"http://{}:{}/{}",
self.secrets.config().host,
self.secrets.config().port,
endpoint
)
}

async fn make_api_call(&self, endpoint: &str) -> Result<String, String> {
let url = self.url(endpoint);

// Use the constructed URL for your API call
todo!("Implement request to postgres url {}", url)
}
}

The configuration system enables you to build flexible, secure, and reusable applets that can adapt to different deployment environments while keeping sensitive information protected.