Skip to content

Features planned #3

Description

@dipu-bd

Planned work, roughly in priority order. Each item is self-contained — read
AGENTS.md and the named files before starting one.

Core — extend what the pool is for

  • Authentication — the API can restart instances and resize the pool and has none, which is
    why 8080 has to stay on loopback. Token (and/or basic) auth in internal/server, covering the
    whole mux; decide explicitly whether /health and /metrics stay open for probes and scrapers.
    The dashboard needs a login screen, and the credential has to reach GET /api/stream too —
    EventSource cannot set headers, so that means a cookie or a query token.

  • Standalone local package (docker is best fit. dropping it)

  • API docs — Currently it lives in the repository only. show it in dashboard page as well.

  • Additional Tor network configuration — the torrc surface in internal/tor/torrc.go and
    internal/config covers exits, dirtiness, conflux and pinning. Still missing: entry guards,
    MiddleNodes, IPv6 exits (ClientUseIPv6), and a DNSPort per pool for clients that cannot do
    socks5h. Keep every new knob validated in internal/config and documented in .env.example and
    docs/configuration.md.

  • Bridges and pluggable transports — obfs4 / snowflake / meek, for hosts where direct Tor is
    blocked. Note this is not only config: obfs4proxy and friends are not in the image, so
    docker/Dockerfile changes too. Bridge lines are secrets-ish — do not log them.

  • Show instance logs in dashboard — tor's own output is already captured as
    EventTorLog in internal/stats/events.go. Needs a per-instance filter on the ring, an API
    endpoint (or a filter param on /api/events), and a log view in web/src/views. Decide the
    retention: the ring is small and shared with the audit log, so tor at debug level will evict
    everything else — a separate, larger ring for logs is probably right.

  • Per-session exit policy (country / relay group) — today TOR_EXIT_NODES is global and
    boot-time, so no caller can ask for where it exits. Two viable designs: tag instances into
    groups at spawn (POOL_GROUPS=us:2,de:2,any:1) and constrain pick() in internal/pool/picker.go
    by a group taken from the session key prefix or an API hint; or SETCONF ExitNodes on one instance
    at runtime (see the tor-control skill). Groups are simpler and do not fight the remediation
    ladder. Either way pick() must still fall back rather than refuse to route when a group is empty.

  • Typed failure reasons — handleSessionFailure in internal/server/api.go takes reason as
    opaque text and every report weighs the same. Type it (rate_limited | blocked | captcha |
    transport | other) and let internal/pool/health.go weigh them: a 429 says slow down and the
    exit is fine, a captcha says the exit is burnt and should quarantine well before
    QUARANTINE_FAILURES. Keep a bodyless POST working — it is the documented minimum signal.

  • Per-target exit reputation — the pool records that an instance failed, never for whom.
    Keep (target host, exit IP) burn pairs so a rotation for site X avoids exits already burnt on X
    while site Y keeps using them. Fixes the "everything is quarantined" case in
    docs/operations.md where the instances are not bad, just bad for
    one target. Needs the failure report to carry the host, bounded storage (untrusted input, same rule
    as session keys), and expiry — a burn is not permanent.

  • Active exit probing — failures are only learned reactively, so an idle instance's
    exit_confirmed stays false until real traffic happens to hit it and a dead exit is discovered by
    a user's request. Probe idle instances periodically through their own SOCKS port: confirms the exit
    IP, gives a latency figure, catches wedged exits early, and catches two instances landing on the
    same exit relay (fewer identities than POOL_SIZE claims). Must respect invariant 14 — probing
    runs on its own goroutine and never blocks a request path — and probe failures must not be scored
    the same as real traffic.

  • Weighted instance picking — pick() is fewest-sessions only, so a healthy-but-crawling
    instance takes an equal share. Factor in latency and recent success rate once probing supplies
    them. Keep the random tie-break; an all-equal pool at startup must not funnel every session onto
    one instance.

  • Warm spare instances — every instance serves, so a rotation or wipe-restart shrinks capacity
    exactly when it is needed. SPARE_COUNT instances, bootstrapped and holding circuits but never
    assigned, make rotation and remediation free. The fleet already resizes at runtime, so this is
    mostly accounting in internal/pool plus keeping spares out of pick().

Operational

  • Runtime config editing — every knob is env-only, so retuning a threshold for an aggressive
    target costs a container restart and every bootstrapped circuit with it. PATCH /api/config over
    the tunable subset — thresholds, windows, TTL, DRAIN_ON_ROTATE — never ports or pool identity,
    plus a settings panel in the dashboard. Land this after authentication.

  • Persist stats, events and logs — the rings and the audit log are in memory and reset on
    every upgrade, so the dashboard's history is only ever as old as the container.
    docs/architecture.md already notes this is contained behind
    internal/stats. SQLite or periodic JSON snapshots on the existing volume; session pinnings are
    deliberately not included.

  • Close keep-alives on rotate — invariant 4 calls the pooled-connection bug the most common
    integration bug and pushes the fix onto every client. For the HTTP proxy we can just fix it: mark a
    session on rotate and inject Connection: close on the next response over its old connections
    (internal/proxy/http.go). SOCKS cannot be helped, but this removes a third of the footgun.

  • Event webhooks — stats.Log already supports subscribers and the SSE stream uses them. A
    WEBHOOK_URL that POSTs quarantine / all-down / pool-degraded events means ops learn without
    polling /metrics. Must be fire-and-forget with a timeout — a slow webhook cannot stall the event
    loop.

  • Per-session rate and byte limits — anyone who can reach the proxy ports can burn our Tor
    bandwidth, and MAX_SESSIONS bounds the session table but nothing bounds usage. Per-session
    concurrency cap and byte quota, so one greedy session cannot starve the others sharing its
    instance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions