Tabbycat Adjcore Toolkit

Getting access to your tab

There is a key, and it is easier to find than most people expect — it is on your tab's home page. This page covers where it is, what these tools do and do not read with it, and what to say to whoever controls your tournament's tab.

The short version. Sign in to your tab in a browser. Go to Change Password on the home page. Your API token is there. Copy it into .env as TABBY_TOKEN and you are done.

Where the key is

Tabbycat generates an API token for every user account automatically. From Tabbycat's own documentation:

“To grant administrator access to an application, you can give it your token, which can be found under Tokens in the database or under Change Password on the site home page. Each user has a token automatically generated when registered.”

The second of those is the one to use. Change Password is an ordinary page that any signed-in user can open, so it works whether your tournament is self-hosted or on a managed host — and it does not need the Edit Database area, which managed hosts often close off to tournament accounts.

Prefer the token over your password, for the same reason you would prefer a key to a lock: you can revoke a token on its own, from that same page, without changing anything else you sign in to.

# .env
export TABBY_TOKEN=938fab3...

When you need your password as well

Two things the toolkit reads are not part of the API. Tabbycat's feedback-progress table and its check-in status are ordinary admin pages, and a token authenticates the API rather than a page — so those two need your ordinary sign-in too.

ToolToken aloneWhat you gain by adding a password
The fold and simulatorEverything worksNothing
Judge feedbackEverything worksNothing
Tester trackingEverything except two columns “Owes feedback” and “checked in”

So: start with the token. Add TABBY_USER and TABBY_PASS only if you want those two columns.

Why those two pages are different Technical

Tabbycat's REST API does not expose feedback progress or check-in status. Both exist only as admin pages — but every Tabbycat admin page hydrates its tables from a window.vueData global, so parsing that global out of the HTML gives you the same structured rows the page itself renders. It is still a GET of a page you are allowed to open in a browser; it is just not an API call, which is why a bearer token does not authenticate it.

TabRead.needs_admin_pages() reports whether the client has a session, and tester-tracking/pull.py skips those two reads with a message rather than letting a 403 look like a fault. The fake Tabbycat in demo/ enforces the same distinction — its admin routes refuse a token-only client — so the behaviour is exercised by the demo rather than discovered at a tournament.

Why this is for adjudication cores only

Whichever way you sign in, it has to be a tab-side account — adjudication core or tabulation. This is not a licensing decision; it is what the data requires. A public or participant account cannot see:

If you are not on the adjudication core or the tab team at your tournament, the person who is will need to run these, or give you an account. There is no version of this that works from the public tab alone.

What to send your tab director

You will probably have to ask for an account, and “can I have tab access so I can run some scripts” is a reasonable thing to be cautious about. So here is the case, in a form you can forward:

What it reads. Rounds, the draw, panels, break categories and the break, teams, judges, institutions, motions and written feedback — through Tabbycat's own API, plus two admin pages for feedback progress and check-in status.

What it writes. Nothing. It cannot. The single piece of code that talks to Tabbycat refuses any request that is not a GET, HEAD or OPTIONS before it leaves the machine — there is no write function in the toolkit to call, and no setting that enables one. The only exception is the sign-in form itself, if you use a password rather than a token.

Where the credentials live. In a file on the laptop running it, which is excluded from version control. They are never sent anywhere except to your own tab.

How to check any of that. python3 tests/test_release.py in the repository. Among other things it parses every Python file and asserts that nothing outside the sign-in form can write to a tab.

The line that enforces it Technical

If your TD would rather read the code than the claim, it is short. In core/tabread.py:

SAFE = {"GET", "HEAD", "OPTIONS"}

class _GetOnlySession(requests.Session):
    def request(self, method, url, *a, **kw):
        if str(method).upper() not in SAFE:
            raise ReadOnlyViolation(...)
        return super().request(method, url, *a, **kw)

Every call in requests funnels through Session.request, including the convenience methods, so overriding it is sufficient rather than merely discouraging. A grep is enough to check nothing else is going on:

grep -rn "\.post\|\.patch\|\.put\|\.delete" --include=*.py .

One warning that matters more than the rest

Tabbycat's API returns each participant's url_key, and that value is their private URL. Anyone holding it can submit ballots and feedback as that person.

So any file you save from a pull is credential material, not just data. The feedback tool needs those keys — that is how it addresses a page to a judge — and it keeps them in feedback/data/, which is excluded from version control and written owner-readable only. Delete that directory when your tournament is over.

Do not put a raw pull in a shared drive, a git repository, or a chat.

Technical This is why the published feedback site is addressed by sha256(url_key) rather than by the key itself. The site never contains a key, so a copy of the published directory is not a set of credentials the way a raw pull is; and there is no index, listing or search, so holding the site tells you nothing about who is in it. The hashing happens in the reader's browser.

Things that will waste your afternoon

What you seeWhat it is
404 on an API path Nearly always the slug. It is the tournament's own bit of the web address, the part straight after the host name.
/api/v1/ returns 404 but /api/v1 works Not a mistake — Tabbycat really does 404 on the trailing slash. The toolkit already gets this right; it matters if you are writing your own requests.
A list comes back short Tabbycat pages long lists and puts the next page in a Link: …; rel="next" header rather than in the body. Ignore the header and you silently get the first page only.
403 Forbidden after a successful sign-in The account is not tab-side. See above — this is the one that cannot be worked around.
“did not look like a Tabbycat login page” The url points at a tournament path or a front page instead of the site root. No trailing slash, no /admin.

Full API documentation is Tabbycat's, not ours: the API guide, and your own tab serves its live schema at /api/schema.yml.


Next: set it up →