Remove hardcoded secrets; source credentials from configuration

The Telegram bot token was a const in AddChannels(), and the Mistral API
key was baked into the embedded Provider.Mistral.toml resource.

- Wire AddChannels() through the existing (previously unused)
  AddTelegramOptions(), which binds "Channels:Telegram" and validates on
  start. TelegramOptions is resolved from IOptions<> so TelegramAdapter
  and TelegramChannel are unchanged.
- Ship the embedded provider defaults with an empty ApiKey. Ollama's
  placeholder is now a non-secret literal.
- Re-apply AddEnvironmentVariables() last in AddLunaConfiguration() so
  env vars override the TOML sources; without this the embedded defaults
  would shadow injected credentials.

Credentials are now supplied via Providers__Mistral__ApiKey and
Channels__Telegram__BotToken, or via ~/.luna/providers/*.toml overrides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 07:59:05 +02:00
co-authored by Claude Opus 5
parent 2a07e07180
commit 9f1e62fdbf
5 changed files with 16 additions and 10 deletions
@@ -1,25 +1,27 @@
using Luna.Channels.Abstractions;
using Luna.Channels.Telegram;
using Luna.Configuration;
using Luna.Configuration.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Telegram.Bot;
namespace Luna.Channels.Extensions;
public static class ServiceCollectionExtensions
{
private const string TelegramBotToken = "REDACTED-TELEGRAM-BOT-TOKEN";
extension(IServiceCollection services)
{
public IServiceCollection AddChannels()
{
services.AddSingleton<IChannelManager, ChannelManager>();
// Telegram
var telegramOptions = new TelegramOptions { BotToken = TelegramBotToken };
services.AddSingleton(telegramOptions);
services.AddSingleton<ITelegramBotClient>(_ => new TelegramBotClient(TelegramBotToken));
// Telegram — BotToken is bound from configuration ("Channels:Telegram"),
// supplied via the Channels__Telegram__BotToken environment variable.
services.AddTelegramOptions();
services.AddSingleton(provider => provider.GetRequiredService<IOptions<TelegramOptions>>().Value);
services.AddSingleton<ITelegramBotClient>(provider =>
new TelegramBotClient(provider.GetRequiredService<TelegramOptions>().BotToken));
services.AddHostedService<TelegramAdapter>();
return services;
@@ -34,6 +34,11 @@ public static class ConfigurationBuilderExtensions
builder.Add(new ProviderOptionsConfigurationSource(searchPath));
}
// Re-applied last so environment variables take precedence over the TOML
// sources above. This is how secrets (API keys, bot tokens) are supplied —
// the embedded defaults ship with empty credentials.
builder.AddEnvironmentVariables();
return builder;
}
}
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.5" />
@@ -1,5 +1,4 @@
ApiKey = "REDACTED-MISTRAL-API-KEY"
ApiKey = ""
ApiUrl = "https://api.mistral.ai/v1/"
Models = ["mistral-small-latest"]
@@ -1,5 +1,4 @@
ApiKey = "apfelkuchen"
ApiKey = "ollama"
ApiUrl = "http://localhost:11434/"
Models = ["mistral-nemo:12b"]