Skip to content

Releases: jedisolana/board-of-directors

Board of Directors 0.1.1

Choose a tag to compare

@jedisolana jedisolana released this 06 Sep 05:16

On PyPI: https://pypi.org/project/jedi-board-of-directors/

pipx install jedi-board-of-directors && board

On PyPI, under a different name

pip install board-of-directors was never going to work: PyPI compares a proposed name against
existing ones with the separators removed, and boardofdirectors is already taken by an
unrelated project. The install name is now jedi-board-of-directors. The commands are
unchanged - board and board-of-directors - and so is the import, boardofdirectors.

Releases are cut by tagging. The workflow runs the test suite, refuses a tag that disagrees with
the version here, refuses a wheel that is missing the console or the model list, and uploads
with no API token in existence: PyPI verifies the repository and workflow directly.

The licence named a project that does not exist

It read freeboard contributors, a name left over from before this one. It now reads
jedisolana, and there is a test that walks every shipped document and source file and fails on
a stray date or year - the check that would have caught it.

Security review (CodeQL)

Code scanning was switched on and raised twenty alerts. Two were real and both are fixed; the
other eighteen are guards the scanner cannot see through, and each now has a test that tries to
get past it.

  • The CI workflow declared no permissions, so its token inherited the repository default.
    It now states contents: read, which is all a checkout-and-test job needs.
  • A short secret showed four of its characters. mask() reveals the public sk-or-v1
    prefix and four trailing characters of a real key, which is worth nothing to a stranger — but
    the same rule applied to a sixteen-character string gives away a quarter of it. Short values
    are now shown as ****.
  • Session ids and patch paths were already contained, and the tests that say so were thin.
    They now attack with URL encoding, a null byte, Windows separators, the doubled-dot trick,
    and a symlink pointing out of the folder. Every one is refused or stripped.
  • SECURITY.md says what is deliberately not restricted: the folder you point the board
    at is any folder you like, because you are the one naming it — and the console listens on
    loopback only, which is what makes that safe.

Board of Directors 0.1.0

Choose a tag to compare

@jedisolana jedisolana released this 05 Sep 17:02

Released as Board of Directors.

First release.

The board

  • Independent answers from models in disjoint families — at most one seat per company,
    because a jury of one family is one model with a stutter.
  • Blind peer ranking: members see each other as "Member A", "Member B", so a name on an
    answer cannot move a ranking. The mapping is kept for the audit trail.
  • A chair that did not vote synthesises the decision, and falls back to another model if
    the first refuses rather than discarding answers that were already given.
  • A member who failed is not a member who agreed. Failures are counted and named; too few
    answers returns NO QUORUM instead of a confident-looking result from the survivors.
  • Two kinds of question: decide (a jury) and make (a competition — every member
    attempts the task, the ranking judges whether they actually did it, the chair delivers the
    winning attempt).

The console

  • Runs on 127.0.0.1 from your own machine, so it can hold your key without publishing it.
  • One conversation, two modes: a single model at one request a turn, switching to the board
    on any turn and back again. Only the chair's verdict enters the history.
  • The cost of the next turn is shown before you send it.
  • Live free-model list, sortable by coding or thinking benchmark, with the models that are
    free-priced but not board material filtered out.
  • The board and saved-sessions panels fold away, so the model list can take the whole pane
    while you browse. The fold is remembered.

Recipes

  • Eight presets on top of the same engine, one call each. Competitions: dream (several
    companies' models each dream on a theme; you keep all of them), brainstorm (an idea-making
    team), build (a code-making team), red_team (six attackers, one plan). Juries:
    check_idea, review, audit (over a folder, refusing one that holds secrets). And
    supply_chain — not a board at all: a different model works each step and hands down the line.
  • "Send anyway" now means it. The console's override for a folder with secrets passed the
    server and was refused again by the engine's own seam. The engine takes the override as an
    explicit argument; the server and the recipes pass it through.

Reading code

  • Point it at a folder; it packs the files into one message and asks one model or the board.
    A loader, not a harness — you choose what it reads.
  • Every file is scanned for secrets first. A folder with findings is refused, with an explicit
    per-send override.

The audit before release

Two deep passes over the finished program, hunting the mistakes a first release ships with.
Eleven found, each one fixed with a test that fails without the fix:

  • A pip install served a 404. The console page was never packaged; every test ran
    against the source tree. CI now builds the wheel, installs it clean, and asks the installed
    copy for a page.
  • Paid models could be called with paid switched off — through the everyday chat turn and
    through the OpenAI-compatible endpoint, with the spend cap at $0.00. Both doors now check
    at the request, and a zero cap overrules even an explicit yes.
  • The one endpoint that writes files could write anywhere on disk. Containment is now one
    rule, enforced by the writer itself, symlinks included.
  • The folder scanner read through symlinks pointing outside the folder, which would have
    put a linked credentials file into a prompt. Skipped now, and said so.
  • Every member ranked a line-up containing its own answer, under a prompt promising
    otherwise. Each ranker now judges only the others.
  • A wall of chair failures ran the whole catalogue. Four in a row end the session with
    the members' answers kept.
  • Closing the tab printed a traceback in the terminal. It no longer looks like a crash.
  • Credentials written into a URL (scheme://user:password@host) passed the secret seam.
    Caught now.
  • A stale build directory shipped deleted files in the wheel. The build step cleans first,
    and CI checks the artifact's contents.
  • One tooltip took catalogue data unescaped. Benchmark scores from the feed went straight
    into a title attribute. Scores are now numbers or nothing at the server, and the page
    escapes every interpolated attribute — structurally tested.
  • The library guide documented an attribute that does not exist (scan.count). Every
    example in the guide now runs as part of the test suite, so it cannot drift again.

Honesty about limits

  • Free-tier allowance is read from the account (is_free_tier) rather than asked of the user.
  • The request counter says estimated until a 429 hands over the real number, then keeps
    subtracting from it.
  • Models the catalogue calls free but the API refuses are remembered and not picked again.
  • The daily limit is account-wide: more models buys independence, not headroom. Said plainly.

Watching it happen

  • The board streams: seats fill one at a time, each taking the colour of the vote it
    declared, with a live line saying who is being asked and what stage the session is at.
  • A broken display can never fail a session.

The vote

  • Members declare for / against / conditional; the board counts it and shows one dot per
    seat, the totals, and whether the motion carried.
  • Read from what was declared, never inferred from prose. Undeclared is shown as undeclared.
  • A tie reads as split, not as a decision.
  • The chair is handed the count instead of recounting five essays.
  • The tally persists in saved sessions and in exported minutes.

Paid models

  • Every model on OpenRouter can hold a seat, each with its price on the row. Off by
    default
    ; the permission and the individual send must both allow it.
  • The estimate is shown before you send, rounds up, and a spend cap refuses anything over
    it. Unpriced models are refused rather than costed at zero.
  • The chair follows the members' permission, so it can never turn a free session paid.
  • Your balance is read from OpenRouter with an ordinary key.
  • Routers (openrouter/*) can never hold a seat: two of them can pick the same underlying
    model and one-seat-per-family would guarantee nothing.

The board

  • Six seats by default, with a picker that FILLS the board rather than asking you to tick
    checkboxes, and an edit panel for choosing who sits in each one. The ceiling is the number of companies available,
    since one seat per family means there is no honest board bigger than that.

Three model tiers

  • A search box, because 400 models in a scroller is not a way to find one.
  • Every paid model shows its price, plus what one call costs in cents, and a cheapest sort.
  • free only · paid only · both. Paid-only is a real want: somebody paying for quality may
    not want free models on the board at all.
  • An unrecognised tier falls back to free — a typo must not silently widen what may be spent.

Locking spending off

  • A $0.00 cap makes spending impossible, not merely unselected — it overrules the paid
    toggle and any saved board, because someone who locked spending has said so about their
    money, not about a checkbox.
  • For anyone who bought the $10 purely for the 20× rate limit: the balance is a key, not a
    wallet, and the header says so.

Honest metering

  • Optional management key makes the request meter exact, reading OpenRouter's own
    analytics instead of counting locally. Opt-in, stored separately, and only ever sent to the
    analytics endpoint to read.
  • Retries no longer count as separate requests, and a 429 from an upstream provider is
    told apart from OpenRouter's own limit — it costs nothing and does not move the meter.
  • board reset-count (and a button) starts the meter clean, because a count that was already
    wrong is not repaired by counting correctly afterwards.
  • The ledger is never clamped; going past the allowance is said in words, not hidden.

An OpenAI-compatible endpoint

  • POST /v1/chat/completions with model: "board" — any OpenAI client gets a whole board's
    decision in the shape it already parses. board:make, board:3 select the shape.
  • usage reports requests, not guessed tokens. A board object carries every member's
    answer, the tally, and who failed. No quorum is a 409, not a confident 200.

Local server hardening

  • Cross-site Origin and non-loopback Host headers are refused: loopback keeps the network
    out, but not a web page you visit, and not DNS rebinding.

Writing changes

  • Describe a task and the board writes the fix, returned as a diff per file with an apply
    button. Nothing is written until you press it, one file at a time.
  • Path traversal, files the board never saw, and files that moved since the scan are all
    refused. The previous contents are kept before every write.

Sessions

  • Conversations are saved locally and reopen with the whole proceeding — every member's
    answer, every failure and its reason, the chair — not just the verdict.
  • Export a session as markdown for a pull request or a decision log.
  • Renaming the project carries an existing install's key, tier and call count across.

Keeping the key out

  • The key lives outside the repo by construction — there is no path by which committing the
    project commits your credentials.
  • A pre-commit hook reads staged content and refuses a commit carrying anything
    credential-shaped.

Documentation

  • README.md is the product; docs/library.md is the Python API.
  • Score tabs say how many models they can rank, and separate scored from unscored — the
    published scores are sparse and they change.

Audited before release

  • Atomic writes everywhere. Every store shared one temp filename; under the parallel board
    twelve threads lost 91% of their writes to the file holding the API key. Unique scratch
    names, fsync before rename, a thread lock and an floc...
Read more