{% extends "base.html" %} {% block title %}Settings — Nullius{% endblock %} {% block crumb %}/ settings{% endblock %} {% block head %}{% endblock %} {% block body %} {#- A section per thing a run needs - the model providers, the judge, the run hosts - and the file they are kept in, one at a time from the menu beside them; the strip above says what a run can use today. The menu's entries are links to the sections, so without a script every section shows. -#} {% set by = {} %}{% set held = [] %} {% for group, rows in groups %}{% for r in rows %}{% set _ = by.update({r.name: r}) %}{% if r.source != 'unset' %}{% set _ = held.append(r.name) %}{% endif %}{% endfor %}{% endfor %} {% set marks = url_for('static', filename='brand/marks.svg') %} {% set hosts_row = by.get('HOSTS') %} {% set chosen = (hosts_row.shown.split(',') | map('trim') | select | list) if hosts_row else [] %} {% set providers = [('typesafe', 'TypeSafe AI', 'https://api.typesafe.ai/v1/systemone', [('TYPESAFE_API_KEY', 'Key')]), ('openrouter', 'OpenRouter', 'https://openrouter.ai/api/v1', [('OPENROUTER_API_KEY', 'Key')]), ('openai', 'OpenAI', 'https://api.openai.com/v1', [('OPENAI_API_KEY', 'Key')]), ('anthropic', 'Anthropic', 'https://api.anthropic.com/v1', [('ANTHROPIC_API_KEY', 'Key')]), ('local', 'Local or self-hosted', '', [('MODEL_API_URL', 'Endpoint'), ('MODEL_API_KEY', 'Key')])] %} {% macro field(r, label, writable, options=none) %}
{% if writable %} {% if r.source != 'unset' %}
{{ r.shown }}
{% endif %} {% if options is not none %}{% for o in options %}{% endfor %}{% endif %} {% if r.source == 'environment' %}

Also set in the process environment: the running process keeps that value until it is restarted; the file applies then.

{% endif %} {% if r.shadowed %}

The file holds a different value, shadowed by the environment.

{% endif %} {% if r.advice %}

{{ r.advice }}

{% endif %} {% else %} {{ r.shown or 'not set' }} {% endif %}
{% endmacro %}

Settings

Ready to run {% for label, name in [('OpenRouter', 'OPENROUTER_API_KEY'), ('OpenAI', 'OPENAI_API_KEY'), ('Anthropic', 'ANTHROPIC_API_KEY'), ('Local endpoint', 'MODEL_API_URL')] %} {{ label }}{{ ': set' if name in held else ': not set' }} {% endfor %} {% for jj, name in [(judges[0], 'System One'), (judges[1], 'LLM judge')] %} {{ name }}{{ ': set' if jj else ': not set' }}{% endfor %} {% for item in chosen %} {{ item.split('=', 1)[0] if '=' in item.split('@', 1)[0] else item.split(':/')[0].split(':~')[0] }}{% else %} no run host{% endfor %}
{% if not writable %}
Read-only from here. Settings are written from a browser on the application host only.
{% endif %} {% if unread %}
Not read by Nullius. {{ env_file }} holds {% for u in unread %}{{ u.name }}{% if u.startup %} (Nullius reads {{ u.instead }} at start){% elif u.instead %} (this page reads {{ u.instead }}){% endif %}{{ ', ' if not loop.last }}{% endfor %}. No setting reads {{ 'it' if unread|length == 1 else 'them' }}.{% set passed = unread|selectattr('inherited')|map(attribute='name')|list %}{% if passed %} A run Nullius starts still inherits {% for n in passed %}{{ n }}{{ ', ' if not loop.last }}{% endfor %} in its environment.{% endif %}
{% endif %}
{% if writable %}
{% if embedded %}{% endif %} {% endif %}

Model providers

Manage the providers available for runs and judges.

{% macro provider_logo(key) %}{% if key == 'typesafe' %}{% elif key == 'local' %}{% else %}{% endif %}{% endmacro %} {% for key, title, endpoint, names in providers %} {% set any_set = names | map(attribute=0) | select('in', held) | list %} {% set ready = (names[0][0] in held) %}
{{ provider_logo(key) }}

{{ title }}

{{ ('Endpoint saved' if key == 'local' else 'Key saved') if ready else ('Endpoint required' if any_set else 'Not configured') }}

{% if endpoint %}
Endpoint{{ endpoint }}
{% endif %} {% for name, label in names %}{% if by.get(name) %}{{ field(by[name], label, writable) }}{% endif %}{% endfor %}
{% endfor %}

Judges

Choose the judges that evaluate trial evidence.

{% set s1, llm = judges %} {% macro picker(name, providers, judge, label) %}
{% for key, d in providers.items() %} {% set available = (d.get('url_var') or d['key']) in held %} {% endfor %}

No compatible provider added. Add a provider

{% endmacro %} {% set srow = by.get('SYSTEM_ONE_PROVIDER') %} {% if srow and by.get('SYSTEM_ONE_MODEL') %}
System One
{{ picker('SYSTEM_ONE_PROVIDER', s1_providers, s1, 'Where System One is asked') }}
{{ field(by['SYSTEM_ONE_MODEL'], 'Model', writable, s1_providers[s1.provider].models) }}
{% endif %} {% set prow = by.get('LLM_JUDGE_PROVIDER') %} {% if prow and by.get('LLM_JUDGE_MODEL') %}
LLM judge
{{ picker('LLM_JUDGE_PROVIDER', llm_providers, llm, "The LLM judge's provider") }}
{{ field(by['LLM_JUDGE_MODEL'], 'Model', writable, []) }}
{% endif %}

Agent keys

Some agents may require additional keys.

{% for name in agent_keys %}{% if by.get(name) %}
{{ name }}{{ field(by[name], 'Key', writable) }}
{% endif %}{% endfor %} {% if writable %}
Add a key
{% elif not agent_keys %}

No agent key yet.

{% endif %}

Run hosts

Machines a recipe can run on, over ssh. A host's name tests it.

{% if hosts_row %}
{% if writable %}
{% for item in chosen %}{% endfor %}
{% else %}

{{ chosen | join(', ') or '—' }}

{% endif %}

HOSTS. An alias from ~/.ssh/config, or user@host[:port]. Only the name is stored; keys stay where they are.

{% endif %}
{% if writable %}
{% endif %} {# docker on this machine, outside the form: asked when the section opens (sandbox.js); a login goes to docker, never to the file #}

Sandbox

Where agents run, apart from this machine.

DockerChecking…
{# kept in this browser, not in the file: onboarding.js fills the state and answers the buttons #}

General

First steps and guidance.

Onboarding
First steps

Appearance

Choose how Nullius looks on this device.

Theme
{% for theme, label in [('system', 'System'), ('light', 'Light'), ('charcoal', 'Neutral charcoal'), ('graphite', 'Cool graphite'), ('midnight', 'Midnight blue')] %}{% endfor %}

Local data

Active storage locations on this machine.

{% for label, path in storage_paths %}
{{ label }}{{ path }}
{% endfor %}

Read-only paths. Personal data stays outside the installation and survives upgrades.

{% if writable %} {# a host: one of the ssh config's, or by hand — who and where, and how ssh gets in without a prompt. Test before Add; no password is ever stored, one is used once to put this machine's key on the host. #}

Add host

Choose a machine for your runs.

{# The tabs and the config panel exist only when the ssh config names a host; `dialog-panels` wraps the panels either way, or its closing tag below would close an ancestor and take the dialog's own footer — and the script's `[data-cancel]` — out of it. #} {% if ssh_hosts %}
{% endif %}
{% if ssh_hosts %}
{% endif %}
SSH key
More authentication options
{% endif %}
{% endblock %}