controller-backend

Integrate Cartridge Controller into backend services using Node.js, Rust, or headless mode. Use when building server-side applications, game backends, automated bots, or any non-browser environment that needs to execute Starknet transactions. Covers SessionProvider for Node.js, Rust SDK setup, and headless Controller with custom signing keys.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "controller-backend" with this command: npx skills add cartridge-gg/docs/cartridge-gg-docs-controller-backend

Controller Backend Integration

Integrate Controller into server-side applications and automated systems.

Sessions enable pre-approved transactions without manual user approval, making them ideal for automated backends.

Node.js

Uses SessionProvider with file-based session storage.

Installation

pnpm add @cartridge/controller starknet

Setup

import SessionProvider, {
  ControllerError,
} from "@cartridge/controller/session/node";
import { constants } from "starknet";
import path from "path";

const ETH_CONTRACT =
  "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";

async function main() {
  const storagePath =
    process.env.CARTRIDGE_STORAGE_PATH ||
    path.join(process.cwd(), ".cartridge");

  const provider = new SessionProvider({
    rpc: "https://api.cartridge.gg/x/starknet/mainnet",
    chainId: constants.StarknetChainId.SN_MAIN,
    policies: {
      contracts: {
        [ETH_CONTRACT]: {
          methods: [
            {
              name: "approve",
              entrypoint: "approve",
              spender: "0x1234567890abcdef1234567890abcdef12345678",
              amount: "0xffffffffffffffffffffffffffffffff",
              description: "Approve spending of tokens",
            },
            { name: "transfer", entrypoint: "transfer" },
          ],
        },
      },
    },
    basePath: storagePath,
  });

  try {
    const account = await provider.connect();

    if (account) {
      console.log("Account address:", account.address);

      // Example: Transfer ETH
      const amount = "0x0";
      const recipient = account.address; // Replace with actual recipient

      const result = await account.execute([
        {
          contractAddress: ETH_CONTRACT,
          entrypoint: "transfer",
          calldata: [recipient, amount, "0x0"],
        },
      ]);

      console.log("Transaction hash:", result.transaction_hash);
    } else {
      console.log("Please complete the session creation in your browser");
    }
  } catch (error: unknown) {
    const controllerError = error as ControllerError;
    if (controllerError.code) {
      console.error("Session error:", {
        code: controllerError.code,
        message: controllerError.message,
        data: controllerError.data,
      });
    } else {
      console.error("Session error:", error);
    }
  }
}

main().catch(console.error);

Notes

  • basePath specifies session storage directory (must be writable)
  • First run requires browser-based session creation
  • Session persists between runs

Rust

Uses account_sdk crate for native Rust integration.

Installation

[dependencies]
account_sdk = { git = "https://github.com/cartridge-gg/controller-rs.git", package = "account_sdk" }
starknet = "0.10"
tokio = { version = "1", features = ["full"] }

Setup

use account_sdk::{controller::Controller, signers::Signer};
use starknet::{
    accounts::Account,
    providers::Provider,
    signers::SigningKey,
    core::types::FieldElement,
};
use std::env;

#[tokio::main]
async fn main() {
    // Load private key from environment
    let private_key = env::var("PRIVATE_KEY")
        .expect("PRIVATE_KEY must be set");

    let owner = Signer::Starknet(
        SigningKey::from_secret_scalar(
            FieldElement::from_hex_be(&private_key).unwrap()
        )
    );

    let rpc_url = "https://api.cartridge.gg/x/starknet/mainnet";
    let provider = Provider::try_from(rpc_url).unwrap();
    let chain_id = provider.chain_id().await.unwrap();

    let controller = Controller::new(
        "my_app".to_string(),
        "username".to_string(),
        FieldElement::from_hex_be("0xCLASS_HASH").unwrap(),
        rpc_url.parse().unwrap(),
        owner,
        FieldElement::from_hex_be("0xCONTROLLER_ADDRESS").unwrap(),
        chain_id,
    );

    // Deploy if needed
    controller.deploy().await.unwrap();

    // Execute transaction
    let call = starknet::core::types::FunctionCall {
        contract_address: FieldElement::from_hex_be("0xCONTRACT").unwrap(),
        entry_point_selector: starknet::core::utils::get_selector_from_name("transfer").unwrap(),
        calldata: vec![FieldElement::from(123)],
    };

    controller.execute(vec![call], None).await.unwrap();
}

Headless Controller

For server-side execution with custom signing keys.

Use Cases

  • Single owner, multiple accounts
  • Automated game backends (bots, NPCs)
  • Custom key management requirements

C++ Example

Warning: Never commit private keys. Use environment variables or secret managers.

#include "controller.hpp"
#include <cstdlib>

int main() {
    // Load from environment - NEVER hardcode!
    const char* pk = std::getenv("PRIVATE_KEY");
    if (!pk) {
        std::cerr << "PRIVATE_KEY not set" << std::endl;
        return 1;
    }
    std::string private_key(pk);

    auto owner = controller::Owner::init(private_key);

    auto ctrl = controller::Controller::new_headless(
        "my_app",
        "bot_account",
        controller::get_controller_class_hash(controller::Version::kLatest),
        "https://api.cartridge.gg/x/starknet/mainnet",
        owner,
        "0x534e5f4d41494e"  // SN_MAIN
    );

    // Register new account onchain
    ctrl->signup(
        controller::SignerType::kStarknet,
        std::nullopt,
        std::nullopt
    );

    // Execute transaction
    controller::Call call{
        "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
        "transfer",
        {"0xRECIPIENT", "0x100", "0x0"}
    };

    std::string tx_hash = ctrl->execute({call});
    std::cout << "Transaction: " << tx_hash << std::endl;

    return 0;
}

Security Best Practices

Never commit private keys. Use:

  • Environment variables
  • Secret managers (AWS Secrets Manager, HashiCorp Vault)
  • Hardware security modules (HSM)
// Good
const privateKey = process.env.PRIVATE_KEY;

// Bad - NEVER do this
const privateKey = "0x1234...";

Telegram Mini-Apps

Use SessionConnector with Telegram WebApp context.

Integration Flow

  1. Configure session policies
  2. Set up SessionProvider with cloud storage
  3. Handle authentication redirects
  4. Execute transactions via session

Setup

import { SessionConnector } from "@cartridge/connector";
import { constants } from "starknet";

const policies = {
  contracts: {
    "0x...": {
      methods: [{ name: "action", entrypoint: "action" }],
    },
  },
};

const connector = new SessionConnector({
  policies,
  rpc: "https://api.cartridge.gg/x/starknet/mainnet",
  chainId: constants.StarknetChainId.SN_MAIN,
  redirectUrl: window.Telegram?.WebApp?.initData
    ? "https://t.me/MyBot/app"
    : "https://myapp.com/callback",
});

Next.js WebAssembly Configuration

For Telegram mini-apps using Next.js:

// next.config.js
module.exports = {
  webpack: (config) => {
    config.experiments = {
      ...config.experiments,
      asyncWebAssembly: true,
    };
    return config;
  },
};

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

controller-sessions

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-setup

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-react

No summary provided by upstream source.

Repository SourceNeeds Review
General

controller-signers

No summary provided by upstream source.

Repository SourceNeeds Review