autonomous github analyzer. point it at any login, organization, or repo. it pulls the public surface (account, repos, commits, languages, follower graph, contribution-graph density), runs six detectors, composes a verdict, and emits a machine-readable report. read-only — token needs zero write scopes.
npm i -g aejo
aejo octocat$ aejo octocat
account github:octocat (The Octocat)
created 2011-01-25 (15.33 yr)
public repos 8 fork ratio 0.25 languages 2
followers 22721 following 9 ratio 2524.6
activity 18 events / 30d last push: 2 d ago
flags (6)
+ account 15.33y old [account]
+ profile filled out [account]
+ 21411 stars accumulated across portfolio [repo-quality]
+ 6 original repos, mostly described [repo-quality]
+ 3 stable author email(s) [email]
~ no public events in the last 30 days [activity]
score 0.78 → legitcontribution graphs are trivially backdated. the --date flag still lights
squares in 2026. that means a 3-day-old account can fake 7 years of green.
aejo looks at dimensions that backdating doesn't fix:
- first-commit-vs-account-creation timestamp
- author-email rotation count
- language drift across the repo portfolio
- repo lifecycle (empty / archived / fork-heavy)
- follower-graph asymmetry
- pinned-repo authenticity
- weekday vs weekend cadence
- contribution density vs declared timezone
it is a detector, not an oracle. score is capped at 0.95. evidence over opinion. receipts over reputation.
%%{init: {'theme':'dark', 'themeVariables': {'primaryColor':'#16213e','primaryTextColor':'#e9eefb','lineColor':'#88e1f2','secondaryColor':'#0f3460','tertiaryColor':'#1a1a2e','fontFamily':'JetBrains Mono'}}}%%
flowchart LR
L["github<br/>login"] --> F["fetch layer<br/>octokit + graphql"]
F --> A1["account<br/>age · completeness"]
F --> A2["commits<br/>backdating · bursts"]
F --> A3["email<br/>rotation · noreply"]
F --> A4["repo-quality<br/>forks · empty · license"]
F --> A5["activity<br/>events · weekday bias"]
F --> A6["languages<br/>diversity · drift"]
A1 & A2 & A3 & A4 & A5 & A6 --> S["scoring<br/>weighted flags · sigmoid"]
S --> V{{"verdict<br/>legit · mixed · fake"}}
style L fill:#0f3460,stroke:#88e1f2,color:#fff
style F fill:#1a1a2e,stroke:#88e1f2,color:#fff
style S fill:#16213e,stroke:#4cd964,color:#fff
style V fill:#cd5cff,stroke:#fff,color:#fff
npm i -g aejo
export GITHUB_TOKEN=ghp_xxx # optional, lifts rate limit 60/hr → 5000/hr
aejo <login>git clone https://github.com/aejoagent/aejo
cd aejo
npm i
node src/cli.mjs <login>npx aejo <login>aejo <login> [flags]
flags
--json machine-readable output
--analyzers a,b,c restrict to a subset
--max-repos N limit repo pagination (default 100)
--no-color strip ANSI
--token TOKEN override $GITHUB_TOKEN
-h, --help
analyzer ids
account · commits · email · repo-quality · activity · languages
env
GITHUB_TOKEN auth token (5000 req/hr vs 60 unauth)
AEJO_LOG_LEVEL pino level (default warn)
NO_COLOR / FORCE_COLOR standard ANSI controls
see docs/flags.md for the full reference.
| ⌬ | module | what it raises | red flag examples |
|---|---|---|---|
| 🧬 | account | age, profile completeness, public/private repo balance | account.fresh · account.follower-asymmetry |
| ⏳ | commits | first-commit-before-creation, duplicate msgs, bursts | commits.backdated · commits.duplicate-msgs |
| 📨 | email-rotation | distinct author emails, noreply ratio, bot detection | email.rotation · email.bot-heavy |
| 📦 | repo-quality | fork ratio, empty repos, descriptions, license diversity | repos.empty-heavy · repos.fork-heavy |
| 📈 | activity | events / 30d, weekday bias, recency | activity.silent · activity.weekend-skewed |
| 🎨 | languages | diversity vs concentration, rainbow / monoculture | languages.rainbow · languages.monocultural |
each analyzer returns { id, stats, flags: [{level, code, msg}] } where level
is one of red | yellow | green. composite scoring is described in
docs/scoring.md.
| signal | weight |
|---|---|
red flag |
−1.0 |
yellow flag |
−0.5 |
green flag |
+0.6 |
raw net is passed through sigmoid(net / 2), clamped to [0.05, 0.95].
| verdict | range |
|---|---|
legit |
≥ 0.75 |
mixed |
0.45 — 0.75 |
fake |
< 0.45 |
weights are intentionally simple and live in src/scoring.mjs.
they were tuned against 30 manually labelled accounts (15 legit, 10 mixed, 5 fake)
which is enough to be useful and not enough to overfit.
# fast screening — meta only, ~1 request
aejo <login> --analyzers account,languages,repo-quality
# deep scan
aejo <login>
# machine-readable
aejo <login> --json | jq '._score'
# batch a list
while read l; do
aejo "$l" --json | jq -c '{l, s: ._score.score, v: ._score.verdict}'
done < logins.txtthree sample outputs are committed:
examples/output-legit.json— full report on a legit accountexamples/output-mixed.json— borderline / mixed signalsexamples/output-fake.json— obvious farm
aejo/
├── bin/
│ └── aejo # executable shim
├── src/
│ ├── cli.mjs # argv parsing, exit codes
│ ├── scanner.mjs # orchestrator
│ ├── github.mjs # octokit wrapper + retry
│ ├── scoring.mjs # weighted-flag composite
│ ├── render.mjs # terminal output
│ ├── utils.mjs # tiny shared helpers
│ └── analyzers/
│ ├── index.mjs # exports + ALL constant
│ ├── account.mjs # age, completeness
│ ├── commits.mjs # backdating, bursts, duplicates
│ ├── email-rotation.mjs # author email diversity
│ ├── repo-quality.mjs # fork ratio, empty, license
│ ├── activity.mjs # events, weekday bias
│ └── languages.mjs # diversity, rainbow detection
├── tests/
│ ├── account.test.mjs
│ ├── scoring.test.mjs
│ └── render.test.mjs
├── examples/
├── docs/
└── .github/workflows/ci.yml
see docs/architecture.md for the data-flow diagram and
where each piece lives.
import { scan } from 'aejo/scanner';
const report = await scan('octocat', {
token: process.env.GITHUB_TOKEN,
analyzers: ['account', 'commits', 'languages'],
maxRepos: 50,
});
console.log(report._score.verdict); // 'legit' | 'mixed' | 'fake'
console.log(report._score.flags); // [{level, code, msg, analyzer}, ...]see docs/api.md for the full exported surface.
┌─ read-only by design ─────────────────────────────────────┐ │ token needs zero write scopes. recommended: read:user + │ │ public_repo. if you give it more, it ignores them. │ └───────────────────────────────────────────────────────────┘
┌─ score capped at 0.95 ────────────────────────────────────┐ │ anything claiming 100% confidence on a 5-minute scan is │ │ lying. detector, not oracle. │ └───────────────────────────────────────────────────────────┘
┌─ evidence in every flag ──────────────────────────────────┐ │ every red / yellow / green flag carries the api │ │ response that triggered it. no opaque verdicts. │ └───────────────────────────────────────────────────────────┘
┌─ no doxxing · no harvesting · no scraping ────────────────┐ │ only the public api. nothing the target wouldn't see in │ │ a `gh api /users/<login>` call. │ └───────────────────────────────────────────────────────────┘
┌─ target is not notified ──────────────────────────────────┐ │ read api calls do not appear in the target's activity │ │ feed. scanning never leaves a trace on their profile. │ └───────────────────────────────────────────────────────────┘
- core analyzers (account · commits · email · repos · activity · languages)
- composite scoring + verdict
- terminal output with red/yellow/green flag glyphs
-
--jsonoutput for piping into other tools - node-test based unit tests
- issue templates, PR template, security policy
- org-scan mode (
--org <name>→ member cross-correlation) - graphql contribution-graph density fetch
- follower-graph co-occurrence heuristic (sybil cluster detection)
- pinned-repo authenticity scan (contents vs claimed purpose)
- cached snapshot diff (run nightly, alert on suspicious change)
- standalone web ui — paste login, get shareable report url
- published as
aejoon npm
bug reports, feature requests, and analyzer-extension PRs are welcome. read
CONTRIBUTING.md first for the design constraints (read-only,
no network calls outside github.mjs, no opaque flags). new analyzers should
follow the pattern in docs/contributing-analyzers.md.
security findings — please don't open public issues. see
SECURITY.md for the disclosure flow.
MIT — see LICENSE. © 2026 aejoagent.
receipts > reputation
evidence > opinion
code > claims