Sextant is a browser extension that adds analytics and navigation tools to
GitHub pages: a lines-of-code panel, a repo health card, dependency
vulnerability badges, TODO counts, a file-tree sidebar, a pull-request
analyzer, a notification inbox, a permission auditor, an organisation
dashboard, a command palette, and an optional AI assistant. This policy
describes what data the extension stores, what leaves your browser, where it
goes, and what the companion backend at sextant.octago.nl does
with it. Sextant is published by Knaxly Ltd; the extension, this policy and the
terms live at
knaxly.dev/sextant.
Summary
- Your GitHub token is sent to
api.github.comand to the Sextant backend. The backend uses it in memory for one request and discards it. One thing derived from it is kept in the server's memory, filed under a hash of the token and never the token itself: on the clone-based analyses, your numeric GitHub account id for 10 minutes, used to share clone capacity fairly. It is not written to disk or to a log. - If you turn on the AI features and supply an Anthropic API key, the
contents of repository files you attach (including files in
private repositories) plus pull-request diffs, pull-request and issue
bodies, issue comments and workflow-run details are sent to Anthropic.
They travel directly from your browser to
api.anthropic.com; the Sextant backend is not in that path and receives neither the content nor your Anthropic key. What Anthropic receives is unchanged by that; the third-party transfer still happens, on your instruction and against your own key. See AI features. - A list of the repositories, pull requests and issues you visit
(including private ones) is written to
chrome.storage.sync, which your browser replicates to your browser-vendor account. See What's stored. - The third-party line-counting service
api.codetabs.comis sent theowner/repostring, and only for a repository Sextant has positively established is public. If it cannot establish that, the request is skipped. See codetabs.com. - Package names and versions from your dependency manifests are sent to
api.osv.devto look up known vulnerabilities. - No analytics, no telemetry, no advertising, no sale of data.
What's stored on your machine
chrome.storage.local: stays in this browser profile
Written only by the extension, readable only by the extension, never synced by us:
pat: a GitHub Personal Access Token, if you paste one into the Options page.oauthToken: a GitHub OAuth user access token, if you complete the "Connect GitHub" flow.githubLogin: your GitHub login name, shown in the Options page next to "Connected as…".aiKey: your Anthropic API key, if you enter one;aiEnabledandaiModel: the AI on/off switch and the Claude model you selected.aiChatSessions: your AI chat history: up to 20 conversations, each holding your messages, the assistant's replies, the repository the chat was started from, and the list of file paths you attached. Assistant replies routinely quote your source code, so this store can contain private code.aiActiveSessionIdrecords which conversation is open.aiDailyCount/aiDailyDateandaiChatDailyCount/aiChatDailyDate: per-day counters used to enforce the local usage caps (20 PR summaries, 100 chat requests).githubOutageSignal: a short-lived record used to tell you when GitHub is failing rather than Sextant. It holds the shape of recently-failing GitHub endpoints (for examplerepos/*/*/git/ref), a timestamp for each, and whether you dismissed the resulting notice. A shape keeps only GitHub's own API route words, taken from a fixed list; every position that holds a name (the owner, the repository, a branch, a tag, a file path, a pull request or issue number) is replaced with*, and that holds even when the name itself happens to be one of those route words (the repositorygit/gitshapes torepos/*/*like any other). Query strings are dropped entirely. It never holds a repository name, a URL, a token or any response content, and it expires on its own once GitHub starts answering again.- UI state:
treeOpen,sidebarTab,reposTypeFilter,lastAuditOrg(the last organisation you audited),inboxUnreadOnly,inboxParticipating,inboxAutoRefresh.
chrome.storage.sync: replicated by your browser
The ⌘K / Ctrl+K command palette records where you have been so it can offer
"recent" results. Every time you load a GitHub repository, pull request or
issue page, the extension writes an entry to recentVisits in
chrome.storage.sync holding the owner, repository name, item
type, pull-request or issue number, the page URL, and a timestamp. The 60
most recent entries are kept. The organisation dashboard reads the same list
to pre-fill the organisation field.
This is not local-only storage. When browser profile sync is
enabled, Chrome and Edge (the only browsers Sextant supports) replicate
chrome.storage.sync to your Google or Microsoft account and to
your other signed-in devices. Private repository, pull
request and issue names and URLs are included; recording happens
automatically on page load, with no prompt. Sextant does not send this list to
its own backend or to any third party; the replication is performed by your
browser. To keep it out of sync entirely, turn off extension sync in your
browser settings, or use "Clear all" in the Options page to delete it.
"Clear all" in the Options page erases both chrome.storage.local
and chrome.storage.sync. Uninstalling the extension removes its
browser storage.
What's transmitted off your machine
1. GitHub → api.github.com (directly from your browser)
Most Sextant features read GitHub through GitHub's own REST and GraphQL APIs,
from your browser, with your stored token in the
Authorization: Bearer … header. No Sextant server is involved in
the calls listed below, and what comes back stays in your browser, except on
the AI path, where the last two items below are put into a prompt and sent
onward as described in section 6. Separately from this list, the backend makes
its own calls to api.github.com with your token when you use the
features in section 3; those are described there.
The endpoints your browser calls are, by feature:
- Repository metadata and default branch:
/repos/{owner}/{repo}. - Your login name for the Options page, read once when you sign in:
/user. Only the login is kept; see section 7. - File-tree sidebar and command-palette file search:
/repos/{owner}/{repo}/git/ref/heads/{branch}and/repos/{owner}/{repo}/git/trees/{sha}?recursive=1. - CI status and failing-file dots:
/repos/{owner}/{repo}/commits/{sha}/check-runsand/repos/{owner}/{repo}/check-runs/{id}/annotations. - Byte-estimate SLOC fallback:
/repos/{owner}/{repo}/languages. - Repository lists for the Repos tab and the organisation dashboard:
/orgs/{org}/repos,/users/{user}/repos. - Permission audit:
/repos/{owner}/{repo}/collaboratorsand/orgs/{org}/repos. - Notification inbox:
/notificationsand/notifications/threads/{id}. Marking a notification read (PATCH) and "mark all read" (PUT) are writes to your GitHub account, performed when you click those controls. - Branch list:
POST /graphql, and/repos/{owner}/{repo}/git/refs/heads/{name}. - Pull-request diff for the AI summary (only when you click "Summarize
with AI"):
/repos/{owner}/{repo}/pulls/{n}with thediffmedia type. - AI chat context (only when you send a chat message):
/repos/{owner}/{repo}/contents/{path},/pulls/{n},/pulls/{n}/files,/issues/{n},/issues/{n}/comments,/actions/runs/{id},/actions/runs/{id}/jobs,/commits/{sha}/check-runs.
2. Public repositories → codetabs.com (third-party service)
When the Sextant backend is not used or fails, the extension may call
api.codetabs.com directly from your browser for a line-of-code
count. The only data sent is the repository owner and name taken from the page
URL: no GitHub token, no Sextant identifiers, no other metadata. Codetabs is
a third-party service we do not operate or control; see
codetabs.com for their policy.
Order of attempts on a repository page: if you have a token configured the backend is tried first; codetabs is tried next; the GitHub byte estimate is the last resort.
The codetabs call is made only when Sextant has positively determined that the repository is public. The determination is made from the page you are already looking at: GitHub's own repository metadata in the page markup and the Public / Private / Internal label in the repository header, each cross-checked against the owner and repository named in the URL you are on. No extra network request is made to work it out, so establishing visibility cannot itself disclose anything.
The check has three possible answers, and only "public" allows the call. If the page says the repository is private or internal, or if Sextant cannot establish the visibility at all (because the page has not finished loading, because GitHub changed the markup the check reads, or for any other reason), the repository is treated as not public: codetabs is skipped and the extension falls back to the GitHub byte estimate, which uses your own token. If you have no token configured, the panel simply shows no count. "Could not tell" is never treated as "public".
Codetabs is also skipped, whatever the visibility, when the Sextant backend has refused the repository as too large (see Backend refusals): that refusal is about the size of the repository, and codetabs would be counting the same repository.
No token and no repository content is ever sent to codetabs.
3. Repository analysis → sextant.octago.nl
When you have a GitHub token configured, the extension calls
POST https://sextant.octago.nl/analyze (and the deprecated
POST /sloc, kept for older extension versions) with:
- The
ownerandrepostrings, the list of analyses requested, and for pull-request analysis the PR number. - Your GitHub token in the
Authorization: Bearer …header.
These calls happen automatically when you open a repository page or the file sidebar, no click required. The analyses are:
- sloc: resolves the default branch's HEAD commit,
performs a
git clone --depth=1(10 MB per-file cap) into ephemeral storage, runs scc, returns line counts per language, and deletes the clone. A repository GitHub reports as bigger than the backend's size ceiling (1181 MiB by default) is refused before anything is cloned; see Backend refusals. - todos: same clone, counts TODO/FIXME markers and returns per-file counts.
- deps: reads dependency manifests and queries osv.dev (see below).
- health: repository aggregates (stars, forks, last push, commit activity, open issues and pull requests) via GitHub's API, using your token.
- pr: pull-request size, changed-file categories, risk flags, and suggested reviewers. The result names GitHub users (the PR author and suggested reviewers derived from recent commit history) and lists changed file paths.
The backend does not persist your token. Tokens are held in memory for the lifetime of a single request, used to authenticate the GitHub API calls and the clone, and discarded. Cloned repositories are deleted as soon as the analysis finishes. Clone errors are scrubbed before being returned so a token cannot leak in an error string.
For the two analyses that clone (sloc and
todos), the backend makes one additional call to GitHub's
GET /user endpoint with your token, to read your
numeric GitHub account id. The id is used as a fairness key: a
single account may occupy at most two of the backend's four concurrent clone
slots, so that one caller cannot exhaust the service for everyone else. Only
the numeric id is read from GitHub's reply: your login, name, email and the
rest of your profile are not decoded, not logged and not stored. The id is kept
in the server's memory for 10 minutes, in a table bounded to 2,048 entries and
filed under a truncated SHA-256 hash of your token, so that a busy session does
not repeat the call; it is never written to disk and never written to a log. If
the lookup fails, that failure alone is remembered for 30 seconds under the same
hash (no identifier is stored in that case), so a GitHub outage does not
produce one failing call per request.
The call is made only when a clone is actually about to be attempted: it comes
after the repository has been resolved with your token and after the size,
cool-off and disk checks have passed, so a request the backend refuses for
those reasons does not trigger it.
4. Dependency vulnerabilities → api.osv.dev (third-party service)
The deps analysis parses your dependency manifests (for
example package.json, go.mod,
requirements.txt) and sends the
package names, ecosystems and versions in a batch query to
https://api.osv.dev/v1/querybatch, operated by the Open Source
Vulnerabilities project. No source code, no file contents, no repository name
and no token are sent. Note that for a private repository the package names
themselves can reveal internal module paths. This request is made by the
Sextant backend, not by your browser.
5. Byte-estimate fallback → api.github.com
If the backend fails and codetabs was skipped or failed, Sextant calls GitHub's
/repos/{owner}/{repo}/languages endpoint directly from your
browser with your stored token, divides bytes-per-language by an embedded
constants table, and shows the result prefixed with ≈. The
backend is not involved.
6. AI features → api.anthropic.com (third-party service)
The AI features are off by default. They activate only after
you tick "Enable AI" and save your own Anthropic API key
(sk-ant-…) in the Options page. They also require a
connected GitHub account: an OAuth login or a Personal Access
Token. An Anthropic key on its own is not enough, because both features read the
repository from api.github.com with your own credential before they
build a prompt, and without one that read is anonymous and returns nothing at
all from a private repository. The extension refuses up front rather than
sending a prompt with no context. With the AI features enabled, the following
data is sent to Anthropic, a
third-party AI provider, directly from your browser:
- File contents from your repositories, including private repositories. In the AI chat sidebar you pick files with the file picker; when you send a message the extension fetches the current contents of each selected file from GitHub with your token and puts them in the prompt. At most 200 files may be attached and each is truncated at 30 KB, so a message carries up to about 6 MB of file content; a selection too large for one request is split across several, up to twelve.
- Pull-request diffs. "Summarize with AI" on a PR page sends the first 8 KB of the PR's diff, plus the page title.
- Context for the page you are on, fetched automatically. Every chat message you send from a pull-request, issue or workflow-run page silently attaches: for a PR, its title, state, branches, description, the list of changed files with line counts, and check-run results; for an issue, its title, state, labels, assignees, body, and up to 10 comments with their authors' logins; for a workflow run, its name, status, branch, commit SHA, trigger and failed job names. This happens on every send even if you attach no files and the interface does not show it. It is capped at 30 KB per message.
- Your questions and the prior turns of the conversation, which are replayed with each message.
- Your Anthropic API key, in the
x-api-keyheader of the request toapi.anthropic.com.
The request is made by the extension's own background service worker to
https://api.anthropic.com/v1/messages. No Sextant server is
involved: neither the prompt nor your Anthropic key reaches our
infrastructure at any point on this path, and there is nothing for us to
parse, cache, store or log because we never receive it. Your GitHub token is not
sent to Anthropic and is not sent to us here either; it is used only for the
api.github.com reads listed in section 1 that assemble the prompt.
The key is held in the service worker rather than in the scripts Sextant runs on
GitHub pages, so page-level JavaScript cannot read it.
What this does not change is the transfer itself. Anthropic still receives the same file contents, diffs and issue text it received before, on your instruction and billed to your own account. The route is shorter by one hop; the disclosure to a third party is the same disclosure.
One consequence worth stating, because it is visible to you: Anthropic blocks
calls made from a browser for organisations that have configured custom
data-retention settings. If your Anthropic organisation is one of those, the
request fails and Sextant reports that it could not reach
api.anthropic.com. There is no fallback route through our
infrastructure.
Once your data reaches Anthropic it is governed by Anthropic's terms and privacy policy and by the settings on your own Anthropic account, including whatever data-retention configuration that account has. Sextant has no control over it. See anthropic.com/legal/privacy. If you do not want repository content leaving your browser, leave the AI features switched off; every other Sextant feature works without them.
The assistant's replies are stored in your browser in
aiChatSessions (see above) and are not sent to the Sextant
backend for storage.
7. Sign-in → github.com (OAuth device flow)
Clicking "Connect GitHub" runs GitHub's OAuth device flow, in
the extension's own background service worker and against
github.com only. It asks
https://github.com/login/device/code for a short user code, shows
you that code and sends you to GitHub's device-authorisation page to approve it,
then polls https://github.com/login/oauth/access_token until GitHub
returns an access token. The token, and your login name read once from
GET /user, are stored in your browser.
No Sextant server takes any part in signing you in. Nothing about
the sign-in is sent to sextant.octago.nl, and there is no code
exchange, no redirect and no client secret on our side: the device flow uses
none. The only value the extension needs is the OAuth application's
client_id, which is a public identifier and is compiled into the
extension.
OAuth scope
"Connect GitHub" requests the scopes repo,
read:user and read:org.
repogrants full read and write access to every repository your GitHub account can reach, public and private, including code, issues, pull requests and settings. GitHub's classic OAuth has no read-only equivalent, which is why the scope is this broad. Sextant itself only reads repository data; the sole write it makes against your account is marking your GitHub notifications as read, when you click those controls in the inbox. The grant you give GitHub is still read-write, and any code holding that token could write.read:user: reads your login name for display in the Options page.read:org: lists organisation repositories and members for the Repos tab, the organisation dashboard, and the permission audit.
If you want least privilege instead, skip OAuth and paste a fine-grained
Personal Access Token scoped to only the repositories and permissions you
choose. Read-only Contents access is enough for the SLOC, TODO
and dependency features.
What we do not do
- No analytics, telemetry, page tracking, or third-party trackers.
- No advertising, and no sale or sharing of your data for anyone else's purposes.
- No transmission to any service other than the ones named on this page:
api.github.com,github.com(the two device-flow sign-in endpoints in section 7 and nothing else),api.codetabs.com,sextant.octago.nl,api.osv.devandapi.anthropic.com. - No storage of access tokens on the backend, and no receipt of Anthropic API keys by it at all.
- No prompts, replies, file contents or diffs reach the backend, so there is nothing of that kind for it to store.
- No reading of pages outside
github.com. Sextant's content scripts run ongithub.comonly, and are excluded from/login,/sessions/*,/settings/*,/oauth/*and/logout.
Logs
The backend writes operational logs to Azure Container Apps log analytics.
What goes in: one line per request with the HTTP method, the request path
(truncated to 256 characters), the status code, the duration and a small set of
diagnostic flags about how the backend identified the caller for rate-limiting
purposes: how many entries the X-Forwarded-For header carried,
whether the last one could be parsed and whether it carried a port, whether the
backend fell back to the connection's peer address, and whether the address was
IPv4, IPv6 or unparseable. These describe the shape of the address, not
the address: no part of it, in any form, is included. They exist so that a
misconfigured proxy (which would silently collapse rate limiting into a single
shared bucket) is visible to the operator. Also logged: error traces, and,
for GitHub webhook deliveries, the event name, action, plan, delivery id,
and a one-way hash of the sending and purchasing account logins (SHA-256,
truncated to 12 hex characters) so the operator can correlate repeat events
without retaining the plaintext username. A webhook delivery that fails its
signature check also logs the sender's IP address.
What does not go in: request bodies, access tokens, Anthropic API keys,
prompts, AI responses, source code, repository contents, or any plaintext
per-user GitHub identifier. Request paths for the endpoints the extension uses
contain no repository names: owner and repo travel
in the JSON body, which is never logged.
Data retention
The backend caches analysis results so repeated visits do not re-clone. The cache is tiered by sensitivity:
- Public repositories: SLOC counts, TODO counts and the dependency-vulnerability summary are written to disk, keyed by commit SHA. They contain aggregate counts that anyone with read access would compute identically. That disk is a persistent Azure Files share, mounted into every running instance of the backend and shared between them. It is not scratch space: an entry survives restarts, scale-to-zero and the replacement of the service with a new build, and an entry written by one instance can be read by the others. An entry is kept for up to 7 days after it was last used, and every use restarts that clock: this is an idle timer, not a 7-day cap, so an entry for a repository somebody opens regularly can persist indefinitely, for as long as those visits continue. Because the storage is persistent, that idle timer is the actual bound on how long a cached result lives. Last use is recorded on the file itself (to within about an hour, so that a cache hit is not an extra write), so a use registers for every instance and not only the one that served it, and the timer is applied to the whole share rather than to any one instance's view of it. The store is additionally capped, share-wide, at 256 MiB and 20,000 entries, with the least recently used entries deleted first; the share is re-examined and expired entries swept every 30 minutes, so deletion can lag the timer by up to that interval. The operator may also clear the share at any time. Nothing outside this store keeps a further copy: the share has no snapshots and no backup, so an entry the sweeper deletes is gone.
- Private repositories: the line counts and TODO counts are held in an in-process memory cache only, bounded to 200 entries with a 15-minute time-to-live, partitioned per caller by a truncated SHA-256 of the bearer token so one user's entries are never served to another. The dependency-vulnerability summary is not cached at all for a private repository: it is recomputed on every request, and the backend does not read the shared disk cache for it either. Nothing derived from a private repository is written to disk.
- Never cached: repository health, pull-request analysis (it names people), and the dependency-vulnerability summary for private repositories. Nothing on the AI path appears here because none of it reaches the backend to be cached or not cached.
Cloned repositories are deleted immediately after each analysis, on every exit path: success, error, timeout and crash of the request. The clone is written into a scratch directory created owner-readable only. The one case a deletion cannot cover is the whole server process being killed mid-analysis, which leaves a partial checkout on the container's own temporary filesystem; a janitor sweeps that scratch area at startup and every 5 minutes and removes any directory that has been idle for 15 minutes, and that filesystem is discarded when the container is replaced. Clone scratch is not the shared cache volume described above: it is ephemeral, per-instance and never a persistent volume, which is why a repository's files never outlive the analysis that fetched them. Backend logs are retained for up to 30 days for operational diagnostics.
Rate limiting and backend refusals
To keep the backend available, it limits how many requests one caller may make.
Two keys are used: your IP address as the hosting platform
reports it, and a truncated SHA-256 hash of your token. A third,
your numeric GitHub account id, is used only to share the
clone slots for the sloc and todos analyses
(see section 3 above); it is used instead of the token hash there because one
person can create any number of tokens, and counting tokens would let a single
account take every slot. All three live only in the running process's memory,
in bounded tables that discard the least recently seen entries, and all three
disappear when the backend restarts.
None of them is written to disk, and none is written to the
logs. The one place an IP address is logged is a GitHub
webhook delivery that fails its signature check, as described under Logs above.
A caller over the limit gets an HTTP 429 with a Retry-After
header.
A repository analysis can also be refused for reasons that are about the service's resources or the repository itself rather than about your request rate:
- 503 when the service is already running as many repository clones as it allows at once, when one account is already using its share of them, or when an analysis of that same commit recently ran out of time and is inside a short cool-off.
- 507 when the instance is short of scratch disk space for another clone.
- 413 when GitHub reports the repository as larger than the backend's size ceiling (1181 MiB by default). The response names both the reported size and the limit.
The 503 and 507 carry a Retry-After; the 413 does not, because
waiting will not make the repository smaller. In every case the extension falls
back to its other line-count sources, subject to the codetabs gate described in
section 2, and after a 413 skipping codetabs entirely.
Revoking access and deleting your data
- To revoke OAuth: visit github.com/settings/applications and remove "Sextant".
- To revoke a PAT: visit github.com/settings/tokens and delete the relevant token.
- To revoke your Anthropic key: visit the Anthropic console and delete it.
- To delete everything Sextant stores in your browser (tokens, API key, chat history and the synced visit list), click "Clear all" in Sextant's Options page, or uninstall the extension.
Data protection and how to complain
Knaxly Ltd is a UK company and is the data controller for the processing described on this page. That processing is governed by the UK GDPR and the Data Protection Act 2018.
Where the data sits shapes what a request to us can achieve. Your GitHub token, your Anthropic key, your chat history and the synced visit list are held in your own browser and not on our servers: "Clear all" in the Options page deletes them without us being involved, and we cannot read them in the first place. What the backend holds is set out under Logs and Data retention above, and listed again under Your data protection rights: operational logs, analysis results keyed by commit SHA, and the short-lived in-memory tables described in section 3 and under Rate limiting. There is no Sextant account, no profile and no mailing list.
To exercise a data-protection right, or if you think your data has been mishandled, contact us first through the feedback page. You also have the right to complain to the UK supervisory authority, the Information Commissioner's Office, at ico.org.uk/make-a-complaint.
Lawful basis for processing
The UK GDPR requires a lawful basis for each purpose personal data is processed for. The bases below are matched to the processing described above. Where an activity is not ours at all it is listed at the end rather than given a basis it does not need.
Providing the analyses you asked for: contract
Article 6(1)(b): processing necessary to perform a contract with you, or to take steps at your request before entering one. Installing the extension and sending requests to the backend accepts the terms of service, and the following exists only to deliver what those terms describe:
- Receiving the
ownerandrepostrings, the pull-request number and the list of analyses atPOST /analyze(and the deprecatedPOST /sloc), together with your GitHub token in theAuthorizationheader. - Using that token, in memory and for the lifetime of one request, to resolve the repository and its default branch, to perform the shallow clone that sloc and todos run on, and to make the GitHub API calls behind health and pr.
- The calls your browser makes directly to
api.github.comwith your own token, listed in section 1. - Sending package names, ecosystems and versions to
api.osv.devfor the deps analysis, and theowner/repostring toapi.codetabs.comwhen that fallback tier is reached for a repository established as public. - Returning the results to your browser.
None of this is optional enrichment. Without your token the repository cannot be resolved or cloned and there is no analysis to return. Removing the token in the Options page, or uninstalling the extension, stops every one of these requests.
Pull-request analysis, which names other people: legitimate interests
Article 6(1)(f). The pr analysis returns the pull request's author and a list of suggested reviewers derived from recent commit history, so it processes personal data about people other than the one asking. For you that processing sits under the contract basis above; for the people named, the basis is the legitimate interest in the analyser producing a useful review suggestion at all, which it cannot do without naming anyone.
Why that does not override their rights: every name is derived from data your own credential can already read on GitHub (the request fails outright if it cannot read the repository), it is recomputed on each request from that repository's own commit history, returned only to you, and never cached (see Data retention). It is not logged, not combined with anything else, not retained once the response is written, and not used to build a profile of anybody.
Keeping the service available and unblocked: legitimate interests
Article 6(1)(f). Stated plainly, the interest is keeping a free service usable
by everyone who has installed it, and protecting the egress IP address the
backend calls GitHub from against being throttled or blocked because of what one
caller does. Both are
concrete rather than theoretical: a handful of concurrent requests from a single
account can occupy every clone slot in the service, and a stream of invented
tokens becomes a stream of rejected credentials presented to
api.github.com from one address, which is answered by blocking that
address for every user of the service. What is processed to prevent it:
- Your IP address as the hosting platform reports it (IPv6 grouped to a /64), as the key of a rate-limit bucket: a general per-address bucket, a separate one for the readiness probe, and a failed-authentication budget that is charged only when GitHub actually rejects a credential.
- A truncated SHA-256 hash of your token, as a per-token bucket key and as the partition key that keeps one caller's private-repository results from being served to another.
- Your numeric GitHub account id, read once per token per 10 minutes, as the fairness key on the four clone slots (see section 3).
Why this does not override your rights: all of it lives in the running process's memory, in tables with a fixed maximum size, and is discarded when the backend restarts. None of it is written to disk and none of it is written to a log; the single exception is the IP address of a GitHub webhook delivery that fails its signature check, described under Logs. The values are the smallest that do the job: a hash and an opaque integer rather than your name, your login or a stored address. They decide one thing, whether this request proceeds, and are never used to profile you, to target you, or to recognise you anywhere else. The alternative to them is not less processing; it is a service one caller can take away from everybody else.
These controls apply to every request and cannot be switched off for one caller without giving up the protection they exist to provide. If you object to them, the way to stop the processing is to stop sending requests: remove your token, or uninstall the extension. See Your data protection rights.
Operational logs: legitimate interests
Article 6(1)(f). The interest is being able to tell whether the service is working, diagnose faults, notice a misconfigured proxy that would silently collapse rate limiting into one shared bucket, and see abuse for what it is. What goes into a log line, and what is deliberately kept out of it, is set out under Logs above; the records are retained for up to 30 days and contain no request bodies, no credentials, no repository contents and no plaintext per-user identifier: webhook logins are one-way hashed for precisely this reason. Against the interest, that is close to the least an operator can run a service on.
Caching results: legitimate interests
Article 6(1)(f). The interest is not re-cloning and re-scanning the same commit for every visitor: it is the difference between answering in a second and spending minutes of CPU, and it also spares GitHub the repeated fetch. The safeguard is the tiering under Data retention: the persistent disk holds only aggregate counts for public repositories keyed by commit SHA, and an entry records nothing about who asked for it; private-repository aggregates stay in memory for 15 minutes, partitioned by the token hash; and anything that names people or reveals paths in private code is not cached at all.
Webhook deliveries from GitHub: legitimate interests
Article 6(1)(f). Deliveries to the Marketplace webhook are signature-checked and logged as described under Logs: the event name, action, plan, delivery id, and a one-way hash of the sending and purchasing account logins. The interest is operating that listing and being able to correlate repeat deliveries; hashing is what makes that possible without retaining the plaintext usernames.
The AI features: consent
Article 6(1)(a). Everything on the AI path runs on your consent and on nothing
else. The features are off by default: the extension sends
nothing to api.anthropic.com until you have both ticked "Enable AI"
in the Options page and saved your own Anthropic API key, and it re-checks that
switch and that key before every AI request. Ticking the box is a specific,
informed opt-in to one identified flow of data (the file contents you attach,
the pull-request diff when you click "Summarize with AI", the page context
attached automatically to a chat message, and your questions) going from your
browser to Anthropic under your own Anthropic account.
AI features describes that flow in full, including what is
attached without being shown to you.
The basis is stated here even though the transfer no longer passes through our infrastructure, because it is still the extension we publish that assembles the prompt and makes the call, and because the disclosure to a third party is the part that matters to you. What consent buys you has not narrowed: it is the same content, going to the same recipient, and it is still yours to withhold.
You can withdraw consent at any time, as easily as you gave it: untick "Enable AI", or delete the key, in the Options page, or use "Clear all", or uninstall the extension. The next AI request is then not sent at all, because the extension checks both before each one. Withdrawal is not retroactive over data already delivered to Anthropic under your key: that is governed by your Anthropic account and its retention settings, which we do not control. Refusing or withdrawing costs you nothing else: every other Sextant feature works with the AI features off, which is what makes this a real choice rather than a condition of using the product.
One thing this consent is not: it is not a consent anyone can give on someone else's behalf. Content you send may contain other people's personal data, such as the logins of people who commented on an issue you are asking about. That content goes from your browser to Anthropic and never to us, so we neither store, cache nor log it; section 3 of the terms of service puts the responsibility for having the right to disclose it on you.
Processing that is not ours
Some of what this policy describes never reaches us, and giving it a lawful basis of ours would misdescribe who is doing it:
- The
recentVisitslist inchrome.storage.sync. The extension writes it into your own browser's storage; when profile sync is on, your browser replicates it to your Google or Microsoft account under that vendor's terms. It is never sent to the Sextant backend, we cannot read it, and "Clear all" deletes it without any request reaching us. See What's stored. - Everything in
chrome.storage.local: your GitHub token, your Anthropic key, your login name, your AI chat history, the daily counters and the UI state. Your token and your key are transmitted with the requests you make, and that transmission is covered by the bases above, but the stored copies are in your browser profile: we never receive them as stored data and deleting them is something you do without us. - Signing in. The device flow in section 7 runs between
the extension and
github.com; the resulting token is processed by GitHub under its own terms. Nothing about it reaches us, so there is no processing of ours to give a basis to. - The AI request itself. The extension calls
api.anthropic.comfrom your browser and Anthropic is the recipient. Your consent above covers our building the prompt and making the call; what Anthropic then does with the content is governed by its terms and by your account's settings, not by ours.
Your data protection rights
Under the UK GDPR you have the rights set out below. How far each one reaches depends on what we actually hold, so each says what it means here rather than in the abstract.
What we hold that a request could reach
- Operational logs, as described under Logs: one line per request (method, path truncated to 256 characters, status, duration, and flags describing the shape of the client address rather than the address), error traces, and webhook metadata with one-way hashed logins. A webhook delivery that fails its signature check also logs the sender's IP address. Retained for up to 30 days, then gone.
- Short-lived tables in the running process's memory: the rate-limit buckets keyed on your address or on a token hash, the numeric GitHub account id behind a token (10 minutes), and private-repository analysis results (15 minutes, partitioned by token hash). All are bounded in size, none is written to disk, and all are discarded when the backend restarts.
- The shared disk cache: aggregate line, TODO and dependency-vulnerability counts for public repositories, keyed by commit SHA and kept for up to 7 days after their last use. An entry records a repository at a commit; it records nothing about who asked for it.
- Nothing else. There is no Sextant account, no user profile, no order history and no mailing list, and your token, key, chat history and visit list live in your browser rather than on our servers.
The rights
- Access: a copy of your personal data. Most of it is already in your hands: the Options page, and your browser's extension storage, hold all of it. On the backend side we can search the 30-day logs, subject to the identification note below.
- Rectification: correction of inaccurate data. There is little here that can be wrong: we store no name, address, profile or preference about you. The values we do handle are derived (a token hash, an account id), and the way to change them is to change or revoke the credential they come from.
- Erasure: deletion. The browser-side data you delete yourself with "Clear all" or by uninstalling; we are not involved and could not be. The in-memory tables erase themselves within minutes, and on every restart. Log records age out at 30 days, and we will delete a specific record sooner if you can identify it. A cache entry can be deleted on request, though it is a fact about a repository rather than about you.
- Restriction: a pause on processing while a dispute is resolved. Ours is per-request and transient rather than ongoing, so there is no standing record to freeze: in practice restriction means stopping. Remove the token in the Options page, revoke it at GitHub, or uninstall, and no further request is made.
- Portability: a machine-readable copy of data you provided, to take elsewhere. There is no per-user record to export: no account, no history, nothing accumulated over time. What you provided (tokens, key, settings, chat history) is already JSON in your browser, and we hold no copy of it to send you.
- Objection: to processing based on legitimate interests, which here is rate limiting, the abuse controls, logging and caching. They apply to every request and cannot be lifted for one caller without losing the protection, so an objection we upheld would mean not serving your requests, which you can achieve immediately by not sending them. The AI features need no objection: they run on consent, and unticking the box stops them.
- Withdrawing consent: for the AI features, at any time, by unticking "Enable AI" or deleting your Anthropic key. This does not affect processing that has already happened.
- Complaining to the ICO: you may complain to the UK supervisory authority, the Information Commissioner's Office, at ico.org.uk/make-a-complaint, whether or not you raise it with us first. We would rather hear from you first, but that is a preference and not a precondition.
How to exercise a right, and why we may not be able to identify you
Make the request through the feedback page, or to the contact address under Company details below. The feedback tracker is public, so do not put a token, an API key or anything else you would not publish into it. We aim to answer within one month, as the UK GDPR requires.
We usually cannot tell which records, if any, are yours. There is no identifier for you anywhere in the design: no account, no user id, no stored login, no stored address. What reaches the backend is a credential we use and discard, and hashes we cannot reverse. That is a real consequence of building it this way and it cuts both ways: very little about you exists to be disclosed or misused, and equally we cannot pick your rows out of a log that does not name you. The UK GDPR does not require us to collect extra information about you purely so that we can answer such a request, and we will not: asking for identity documents in order to search records that contain no names would create more personal data than it could ever return. If you can point at a specific record (a webhook delivery id, or a time window together with the address a failing delivery came from), we will look for it and act on it. Otherwise the honest answer to an access or erasure request is that we hold nothing we can attribute to you.
EU representative (Article 27)
Knaxly Ltd is established in the United Kingdom, which is outside the European Union. Where a controller established outside the EU offers goods or services to people in the EU, or monitors their behaviour in the EU, Article 27 of the EU GDPR may require it to designate a representative established in the EU in writing, and to publish that representative's identity and contact details.
Our assessment is that it does not, because the EU GDPR does not reach Sextant in the first place. To be clear about what that does and does not mean: Knaxly Ltd is subject to the UK GDPR, which applies to everything described in this policy no matter where you are. The narrower question here is whether the EU GDPR also applies, and with it the Article 27 duty.
Being reachable from the EU is not enough on its own: the test is whether the service is directed at people there. Our reasons for concluding it is not, as at the date at the top of this page:
- Knaxly Ltd has no establishment, branch, staff or agent in the European Union.
- Sextant has a single plan and it is free. There is no price for Sextant, no payment route to Knaxly Ltd, and no pricing in euros anywhere. (The dollar figures in the extension's settings are estimates of what Anthropic may charge you on your own API key if you enable the AI features: a third party's prices, not ours. The £0 figure in the Terms is a liability cap, not a price.)
- It is published in English only, with no EU-language version, and is distributed through the Chrome Web Store's default worldwide listing rather than by targeting any member state.
- We do not advertise or market Sextant in the European Union and hold no EU-country domain.
- Sextant does not monitor behaviour. Nothing it collects is used to track people or build a profile of anybody: the sections above list everything that reaches our backend and how long each item is kept.
This is a reasoned position rather than a permanent fact, and it is the kind of position that can expire quietly. We will revisit it, and appoint a representative if the answer changes, if Sextant gains a paid plan or any pricing in euros, is offered in an EU language, is marketed in a member state, begins profiling users, or if Knaxly Ltd establishes an entity in the Union.
None of this changes how your data is handled. If you are in the EU and use Sextant, everything in this policy applies to you in the same way, including the retention limits and the rights set out below; those do not depend on this assessment. If you want to raise a concern, the feedback page reaches us directly, and nothing here is intended to discourage you from also approaching your own national data-protection authority or the UK Information Commissioner's Office.
Visiting these pages
Everything above is about the extension and the backend. This section is about the five pages at knaxly.dev/sextant themselves, which are what you are reading now.
They are static files with no server of ours behind them. They set no cookies, run no analytics and no tracking script, and have no form: the feedback page routes you to GitHub rather than collecting anything here. We receive nothing when you read them, and we keep no record that you did.
Two parties necessarily see the request, because something has to serve the file: GitHub, which hosts the pages, and Cloudflare, which fronts the domain. Serving a page means receiving the IP address and user agent it was requested with, and both operate their own logs on their own terms, as processors for the hosting we buy from them. Neither is asked for anything beyond delivering the file.
Three of the five pages (this policy's overview, the beta page and the feedback page) load webfonts from Google Fonts, so Google receives the IP address and user agent of those font requests. Each of those pages says so in its own footer. This page and the terms load nothing external at all, which is why the note at the bottom of both says so.
Children
Sextant is a developer tool and is not directed at children under 13. We do not knowingly collect data from them.
Changes to this policy
The "Last updated" date at the top of this page reflects the most recent revision. Material changes to what is collected or where it is sent will be announced on the Sextant page at knaxly.dev/sextant.
Contact
Operator and data controller: Knaxly Ltd.
- Bugs, questions and data-protection requests: the feedback page at knaxly.dev/sextant/feedback.html, which routes to the public issue tracker at github.com/knaxly-dev/sextant-feedback. Filing needs a GitHub account. The tracker is public, so never include a token, an API key, or anything else you would not publish.
- Security vulnerabilities: report them privately at github.com/knaxly-dev/sextant-feedback/security/advisories/new. Do not open a public issue for a vulnerability.
- A written contact address is listed under Company details below.
Company details
Sextant is operated by Knaxly Ltd, a privately owned company registered in the United Kingdom.
- Registered company name: Knaxly Ltd
- Company registration number: 17403507
- Place of registration: England and Wales
- Registered office: 103 Byewaters, Watford, WD18 8WH, United Kingdom
- Contact email: ncr@knaxly.com