Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 1 addition & 17 deletions dappnode/dappnode/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,7 @@ DAppNode Nexus (`https://nexus.dappnode.com`) is DAppNode's own privacy-focused

To configure, use the Setup Wizard at `http://hermes-agent.dappnode:8080` and select Nexus as the provider. Or manually:
1. Sign up at `https://nexus.dappnode.com` and create an API key
2. Add the key to `/opt/data/.env` as `NEXUS_API_KEY=<your-key>`
3. Register Nexus as a named provider in `/opt/data/config.yaml` (current Hermes no longer reads `OPENAI_BASE_URL`, and only sends `OPENAI_API_KEY` to `openai.com` hosts, so a named provider is required):

```yaml
model:
default: "minimax/minimax-m2.7" # browse models at nexus.dappnode.com/models
provider: "nexus"
providers:
nexus:
name: "DAppNode Nexus"
base_url: "https://nexus-api.dappnode.com/v1"
key_env: "NEXUS_API_KEY"
default_model: "minimax/minimax-m2.7"
api_mode: "chat_completions"
```

Verify with `hermes doctor`; the resolved provider source should read `custom_provider:DAppNode Nexus`, not `no-key-required`.
2. In the Setup Wizard or `config.yaml`, set the provider to Nexus with base URL `https://nexus-api.dappnode.com/v1`

## Troubleshooting

Expand Down
4 changes: 2 additions & 2 deletions dappnode_package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hermes-agent.dnp.dappnode.eth",
"version": "0.1.6",
"upstreamVersion": "v2026.6.5",
"version": "0.1.4",
"upstreamVersion": "v2026.5.29.2",
"upstreamRepo": "NousResearch/hermes-agent",
"upstreamArg": "UPSTREAM_VERSION",
"shortDescription": "Self-improving AI agent with multi-LLM support and messaging gateway",
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
context: .
dockerfile: Dockerfile
args:
UPSTREAM_VERSION: v2026.6.5
UPSTREAM_VERSION: v2026.5.29.2
image: hermes-agent.dnp.dappnode.eth:0.1.0
container_name: DAppNodePackage-hermes-agent.dnp.dappnode.eth
restart: unless-stopped
Expand Down
56 changes: 12 additions & 44 deletions setup-wizard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -736,14 +736,11 @@ <h2>Configure ${p.name}</h2>
const env = {};
const p = selectedProvider;
if (p.id === "nexus") {
// Nexus is registered as a named provider in config.yaml (see
// buildConfigYaml). Hermes resolves its key from NEXUS_API_KEY via
// the provider's key_env. We must NOT reuse OPENAI_API_KEY/
// OPENAI_BASE_URL: recent Hermes no longer reads OPENAI_BASE_URL and
// host-gates OPENAI_API_KEY to openai.com, which silently drops the
// key for the nexus-api.dappnode.com host.
const apiKey = (document.getElementById("api-key").value || "").trim();
if (apiKey) env.NEXUS_API_KEY = apiKey;
env.OPENAI_BASE_URL = "https://nexus-api.dappnode.com/v1";
env.OPENAI_API_KEY = apiKey;
env.LLM_MODEL = getModelValue() || "minimax/minimax-m2.7";
} else if (p.id === "ollama") {
const url = (document.getElementById("ollama-url").value || "").trim() || "http://ollama.dappnode:11434";
env.OPENAI_BASE_URL = url + "/v1";
Expand All @@ -755,11 +752,10 @@ <h2>Configure ${p.name}</h2>
const model = (document.getElementById("custom-model-name").value || "").trim();
if (spec === "anthropic") {
if (apiKey) env.ANTHROPIC_API_KEY = apiKey;
if (model) env.LLM_MODEL = "anthropic/" + model;
} else {
// OpenAI-compatible custom endpoint: registered as a named provider
// in config.yaml with key_env CUSTOM_API_KEY. Don't use
// OPENAI_API_KEY — newer Hermes only sends it to openai.com hosts.
if (apiKey) env.CUSTOM_API_KEY = apiKey;
if (apiKey) env.OPENAI_API_KEY = apiKey;
if (model) env.LLM_MODEL = model;
}
} else {
const apiKey = (document.getElementById("api-key").value || "").trim();
Expand Down Expand Up @@ -790,25 +786,10 @@ <h2>Configure ${p.name}</h2>
"# Edit via this wizard, the Terminal tab, or /opt/data/config.yaml directly.",
"", "model:"
];
// Optional top-level `providers:` mapping for OpenAI-compatible endpoints
// that need an API key. Recent Hermes resolves the key from the env var
// named in `key_env` and no longer reads OPENAI_BASE_URL / non-openai.com
// OPENAI_API_KEY, so a named provider entry is required for hosts like
// Nexus or arbitrary custom endpoints.
const providersBlock = [];
if (p.id === "nexus") {
const model = getModelValue() || "minimax/minimax-m2.7";
lines.push(` default: "${model}"`);
lines.push(` provider: "nexus"`);
providersBlock.push(
"", "# Named provider: DAppNode Nexus (key resolved from NEXUS_API_KEY)",
"providers:", " nexus:",
` name: "DAppNode Nexus"`,
` base_url: "https://nexus-api.dappnode.com/v1"`,
` key_env: "NEXUS_API_KEY"`,
` default_model: "${model}"`,
` api_mode: "chat_completions"`
);
lines.push(` default: "${getModelValue() || "minimax/minimax-m2.7"}"`);
lines.push(` provider: "custom"`);
lines.push(` base_url: "https://nexus-api.dappnode.com/v1"`);
} else if (p.id === "ollama") {
const url = (document.getElementById("ollama-url").value || "").trim() || "http://ollama.dappnode:11434";
lines.push(` default: "${getModelValue() || "llama3"}"`);
Expand All @@ -821,30 +802,17 @@ <h2>Configure ${p.name}</h2>
if (spec === "anthropic") {
lines.push(` default: "anthropic/${model || "claude-3-opus-20240229"}"`);
lines.push(` provider: "anthropic"`);
if (baseUrl) lines.push(` base_url: "${baseUrl}"`);
} else {
// OpenAI-compatible custom endpoint → named provider so the API key
// (CUSTOM_API_KEY) is sent to the configured host.
const modelName = model || "gpt-4";
lines.push(` default: "${modelName}"`);
lines.push(` provider: "custom-endpoint"`);
providersBlock.push(
"", "# Named provider: custom OpenAI-compatible endpoint",
"providers:", " custom-endpoint:",
` name: "Custom Endpoint"`,
` base_url: "${baseUrl}"`,
` key_env: "CUSTOM_API_KEY"`,
` default_model: "${modelName}"`,
` api_mode: "${spec === "openai-responses" ? "codex_responses" : "chat_completions"}"`
);
lines.push(` default: "${model || "gpt-4"}"`);
lines.push(` provider: "custom"`);
}
if (baseUrl) lines.push(` base_url: "${baseUrl}"`);
} else {
const model = getModelValue();
if (model) lines.push(` default: "${p.id === "openrouter" ? model : p.id + "/" + model}"`);
lines.push(` provider: "${p.provider}"`);
if (p.id === "openrouter") lines.push(` base_url: "https://openrouter.ai/api/v1"`);
}
lines.push(...providersBlock);
lines.push("", "# Gateway settings (DAppNode)", "gateway:", " port: 3000", " bind: lan");
lines.push(" controlUi:", " dangerouslyAllowHostHeaderOriginFallback: true");
lines.push(" allowInsecureAuth: true", " dangerouslyDisableDeviceAuth: true");
Expand Down
Loading