Files
bare/internal/config/config.go
T
mayatnikovandClaude Opus 5 597c55301c Этап 1: аккаунты — argon2id, сессии, ключевой блоб, вход и регистрация
Сервер: миграция 001 со всей схемой storage.md, store на modernc.org/sqlite
(WAL, foreign_keys, один писатель), фоновая чистка раз в час, argon2id
с параметрами ADR-021 и сверкой constant-time, сессии по SHA-256 токена,
cookie bare_session, глобальная проверка Origin, девять эндпоинтов аккаунта.
Ник в журнал не попадает: для /api/ пишется шаблон маршрута.

Клиент: crypto.js по crypto.md построчно — мастер из пароля, два независимых
ключа из мастера, ключевой блоб с ником в AAD, отпечаток от сырой точки;
db.js со всеми хранилищами версии 1; экран входа и регистрации, настройки
со сменой пароля, выходом и удалением аккаунта.

Пароль не покидает клиент: проверено на боевом сервере — ни пароля, ни priv.d
ни в одном теле запроса, вход на втором устройстве даёт тот же отпечаток.

ADR-027: код internal для 500, причина только в журнале.
ADR-028: тексты состояний клиента сведены в ui.md.
ADR-029: вход под другим ником стирает историю только после подтверждения.
ADR-030: верхняя граница итераций KDF, проверка границ на обеих сторонах.
ADR-031: служебный выход перед повторным входом не заканчивает сеанс.
ADR-032: каталог состояния 0700, файлы базы 0600.

Прямые зависимости: modernc.org/sqlite, golang.org/x/crypto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
2026-08-22 14:06:07 +03:00

74 lines
3.1 KiB
Go

// Package config читает конфигурацию из переменных окружения BARE_* (ADR-022).
package config
import (
"fmt"
"os"
"strings"
)
// Config — всё, что сервер знает о своём окружении.
type Config struct {
Addr string // BARE_ADDR — адрес прослушивания
DB string // BARE_DB — путь к файлу SQLite
Origin string // BARE_ORIGIN — единственный допустимый Origin (ADR-021)
VAPIDPublic string // BARE_VAPID_PUBLIC
VAPIDPrivate string // BARE_VAPID_PRIVATE
VAPIDSubject string // BARE_VAPID_SUBJECT
InviteCode string // BARE_INVITE_CODE — пусто означает открытую регистрацию
}
// Значения по умолчанию — локальный запуск без окружения.
const (
defaultAddr = "127.0.0.1:8411"
defaultDB = "bare.db"
defaultOrigin = "http://127.0.0.1:8411"
)
// Параметры, которые сервер сообщает клиенту в GET /api/config. Это
// константы, а не переменные окружения: их значения — часть криптосистемы
// (ADR-013) и протокола (ADR-021), а не настройка машины.
const (
// KDFIterations — целевое число итераций PBKDF2 на клиенте.
KDFIterations = 1_000_000
// KDFMinIterations — нижняя граница: блоб с меньшим iter сервер не примет.
KDFMinIterations = 600_000
// KDFMaxIterations — верхняя граница (ADR-030). Сервер сам раздаёт iter
// из блоба в GET /api/kdf, и с неподъёмным значением аккаунт нельзя
// ни открыть, ни удалить: обе операции начинаются с PBKDF2.
KDFMaxIterations = 10_000_000
// MaxMessageChars — предел текста сообщения.
MaxMessageChars = 4000
)
// Load читает окружение. Незаданная переменная берёт значение по умолчанию;
// заданная пустой — ошибка: пустой адрес, путь к базе или origin неработоспособны.
func Load() (*Config, error) {
c := &Config{
Addr: env("BARE_ADDR", defaultAddr),
DB: env("BARE_DB", defaultDB),
Origin: env("BARE_ORIGIN", defaultOrigin),
VAPIDPublic: env("BARE_VAPID_PUBLIC", ""),
VAPIDPrivate: env("BARE_VAPID_PRIVATE", ""),
VAPIDSubject: env("BARE_VAPID_SUBJECT", ""),
InviteCode: env("BARE_INVITE_CODE", ""),
}
for _, v := range []struct{ key, value string }{
{"BARE_ADDR", c.Addr},
{"BARE_DB", c.DB},
{"BARE_ORIGIN", c.Origin},
} {
if v.value == "" {
return nil, fmt.Errorf("%s пуст: уберите переменную, чтобы взять значение по умолчанию, или задайте непустое", v.key)
}
}
return c, nil
}
func env(key, fallback string) string {
if v, ok := os.LookupEnv(key); ok {
return strings.TrimSpace(v)
}
return fallback
}