Golem 1.5 features — Part 7: Configuration and Secrets

Introduction

A series showcasing new Golem 1.5 features (releasing end of April 2026) is underway. This installment focuses on configuration and secrets management. Previous parts cover code-first routes, webhooks, MCP, Node.js compatibility, Scala support, and user-defined snapshotting. Check the other Golem-related posts for more information.

Code-first Configuration

Before version 1.5, the standard way to inject configuration values and secrets was through environment variables. The new approach introduces typed configuration that becomes part of an agent’s type definition.

Configuration Implementation

Configuration uses record types that can be nested and are injected into agent constructors:

type DbConfig = {
  host: string;
  port: number;
};

type ExampleConfig = {
  debugLogs: boolean;
  alias?: string;
  database: DbConfig;
};

@agent()
class ExampleAgent extends BaseAgent {
  constructor(
    exampleParam: string,
    readonly config: Config<ExampleConfig>
  ) {
    // ...
  }

  useConfig() {
    const config = this.config.value;
    if (config.debugLogs) {
      console.debug("Debug logs enabled");
    }
  }
}
#[derive(ConfigSchema)]
pub struct DbConfig {
    host: String,
    port: u16
}

#[derive(ConfigSchema)]
pub struct ExampleConfig {
    debug_logs: bool,
    alias: Option<String>,
    database: DbConfig
}

#[agent_definition]
pub trait ExampleAgent {
    fn new(name: String, #[agent_config] config: Config<ExampleConfig>) -> Self;
    fn use_config(&self);
}

struct ExampleAgentImpl {
    config: Config<ExampleConfig>
}

#[agent_implementation]
impl ExampleAgent for ExampleAgentImpl {
    fn new(example_param: String, #[agent_config] config: Config<ExampleConfig>) -> Self {
        Self { config }
    }

    fn use_config(&self) {
        let config = self.config.get();
        if config.debug_logs {
            logging::log(logging::Level::Debug, "example", "Debug logs enabled");
        }
    }
}
final case class DbConfig(
  host: String,
  port: Int
)

object DbConfig {
  implicit val schema: Schema[DbConfig] = Schema.derived
}

final case class ExampleConfig(
  debugLogs: Boolean,
  alias: Option[String],
  database: DbConfig
)

object ExampleConfig {
  implicit val schema: Schema[ExampleConfig] = Schema.derived
}


@agentDefinition()
trait ExampleAgent extends BaseAgent with AgentConfig[ExampleConfig] {
  class Id(val exampleParam: String)

  def useConfig(): Future[Unit]
}

@agentImplementation()
final case class ExampleAgentImpl(exampleParam: String, config: Config[ExampleConfig])
  extends ExampleAgent {

  override def useConfig(): Future[Unit] = {
    val config = config.value
    if (config.debugLogs) {
      js.Dynamic.global.console.debug("Debug logs enabled");
    }
  }
}
#derive.config
pub(all) struct DbConfig {
  host : String
  port : UInt
}

#derive.config
pub(all) struct ExampleConfig {
  debug_logs : Bool
  alias : String?
  database : DbConfig
}

#derive.agent
pub(all) struct ExampleAgent {
  example_param : String
  config : @config.Config[ExampleConfig]
}

fn ExampleAgent::new(
  example_param : String,
  config : @config.Config[ExampleConfig]
) -> ExampleAgent {
  { example_param, config }
}

pub fn ExampleAgent::use_config(self : Self) -> Unit {
  let config = self.config.value
  if config.debug_logs {
    @log.debug("Debug logs enabled")
  }
}

Once configuration requirements are defined in code, agents cannot be deployed without satisfying them. Values are assigned per agent in the application manifest:

agents:
  ExampleAgent:
    config:
      debugLogs: true
      alias: "main"
      database:
        host: "localhost"
        port: 5432

The manifest’s preset feature enables reusable configuration bits applicable to multiple agents or all agents within a component.

Secrets

Secrets represent special configuration that can be updated dynamically (useful for API token rotation). Unlike regular configuration tied to deployments, secrets are accessed via get() to retrieve the latest value:

type DbConfig = {
  host: string;
  port: number;
  password: Secret<string>;
};
#[derive(ConfigSchema)]
pub struct DbConfig {
    host: String,
    port: u16,
    #[config_schema(secret)]
    password: Secret<String>,
}
final case class DbConfig(
  host: String,
  port: Int,
  password: Secret[String]
)
#derive.config
pub(all) struct DbConfig {
  host : String
  port : UInt
  password : @config.Secret[String]
}

Secret values are stored per environment, not per deployment. Initial values can be set via the manifest’s secretDefaults section with environment variable substitution:

secretDefaults:
  local:
    - path: [db, password]
      value: "{{ DB_PASSWORD }}"

CLI commands manage secrets:

golem agent-secret create db.password --secret-type string --secret-value "pwd"
golem agent-secret list
golem agent-secret update-value db.password --secret-value "new-pwd"
golem agent-secret delete db.password

To access current secret values:

const password = config.database.password.get();
let password = config.database.password.get();
val password = config.database.password.get
let password = config.database.password.get!()