sc-audit
SkillSecurityLets your agent run a deep security audit of a smart contract repo or live on-chain contract and report exploitable bugs.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the sc-audit skill
About this capability
Deep security audit of a smart-contract repo OR a live on-chain contract by address - detect Solidity, model the protocol invariants and trust boundaries first, run Slither (best-effort) plus a bounded agentic pass that hunts for a path breaking each invariant, triage, adversarially verify, prove wi
What this skill tells your AI
The instructions your AI receives, as published by aeonfun/aeon in skills/sc-audit/SKILL.md and read by ahel’s review.
${var} - Target selector. Four forms:
- `` (empty) -> auto-select the day's fresh feed target, audit only if it contains Solidity, else exit clean. See §S1 for the selection order (an optional
sc-targets.jsonledger, else thegithub-trendingfeed).owner/repo-> audit that GitHub repo (e.g.Uniswap/v4-core,aave/aave-v3-origin).<chain>:0x<address>-> on-chain mode: audit a live deployed contract by address. It fetches the verified source from the block explorer (Etherscan V2) or Sourcify, materializes it as a Foundry project, and runs the same pipeline - plus on-chain context (proxy/implementation, owner/admin, funds at risk).chainineth/base/arbitrum/optimism/polygon/bsc/... (bare0x<address>defaults toeth). Examples:base:0x4200000000000000000000000000000000000006,eth:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2. On-chain findings are operator-gated - never auto-filed/auto-emailed (see §S7). See §S1/§S2.fixture:<name>-> local regression mode: audit the bundled fixture atskills/sc-audit/fixtures/<name>/(e.g.fixture:vault). No fork, no dedup, no disclosure - a self-contained way to exercise the whole pipeline including the fuzz arm, with no external repo. See §S1/§S2.
Today is ${today}. Read memory/MEMORY.md and the last 30 days of memory/logs/ before starting.
Why this skill exists
Smart-contract bugs are logic and economics, not syntax. Slither matches known static patterns; it is weak on access control, protocol invariants, oracle/price manipulation, rounding/precision, upgradeability storage collisions, and cross-contract reentrancy - the classes that actually drain funds, and where exploitation on-chain is immediate and irreversible. That whole class is what an agentic reviewer catches by reading the source and reasoning about who can call what and which invariant breaks.
This skill is the contract arm split out of vuln-scanner. vuln-scanner detects Solidity and hands the repo here rather than running Slither inline; this skill owns the deep audit and then routes findings through vuln-scanner's shared disclosure machinery. It does not duplicate the disclosure/PVR/email logic - see §S7.
It audits two kinds of target: a GitHub repo (source in a repo you fork), and a live contract deployed on-chain by address (<chain>:0x<addr>) - the latter fetches the verified source from the block explorer/Sourcify and adds on-chain context (proxy/implementation, owner, funds at risk). A live deployed contract is where a bug is already exploitable with real money at stake, so on-chain findings are treated as the highest-stakes disclosure and are operator-gated - staged for a human, never auto-filed (§S7).
The agentic source pass is the reliable core. Slither is best-effort: a headless run has slither allow-listed but not solc / forge / solc-select, so contracts that need a compiler to build may not compile in-run. The source pass needs no compiler, so a clean audit never depends on Slither succeeding.
S1. Detect Solidity and select the target
REPO="${var}" # owner/repo | empty (auto) | fixture:<name> | <chain>:0x<addr> | 0x<addr>
if [ "${REPO#fixture:}" != "$REPO" ]; then MODE=fixture; FIXTURE="${REPO#fixture:}"
elif [[ "$REPO" =~ ^([a-zA-Z0-9-]+:)?0x[0-9a-fA-F]{40}$ ]]; then MODE=onchain # deployed-contract selector
else MODE=repo; fi # owner/repo, or empty -> auto-select
-
MODE=fixture-> audit the bundled fixtureskills/sc-audit/fixtures/$FIXTURE/(see §S2). Skip the dedup ledger and skip disclosure entirely (§S7/§S8) - fixtures are deliberately-vulnerable regression targets meant to be re-run on demand, never disclosed. If the fixture dir is missing, logno-fixture: $FIXTUREand exit clean. -
MODE=repo,$REPOset -> that is the target. Confirm it holds Solidity before forking:gh api /search/code?q=repo:$REPO+extension:sol --jq '.total_count'(or just proceed and detect after clone in S2). If the repo has no*.sol, logno-solidity: $REPOand exit clean - this is the wrong skill for it. -
MODE=repo,$REPOempty -> auto-select a repo target, best-first:- Optional ledger first. If an
sc-source-style ledger exists atmemory/sc-targets.json(schema{updated, repos:[{repo, stars, tier, desc, first_seen}]}, best-first, already Solidity-scoped and deduped), read it and takerepos[0].repo. If that repo was scanned within 30 days permemory/vuln-scanned.json, or turns out to hold no real Solidity after clone (S2), walk downrepos[]. This ledger is optional; the skill stands alone without it. - Trending feed fallback. If no ledger is present or it is exhausted, read the
github-trendingfeed atoutput/.chains/github-trending.md(most recent by ISO header date) and walk its repos for one that contains Solidity. - If none contain Solidity, log
no-solidity-targetand exit clean.
On-chain candidates never enter auto-mode - the empty-
$REPOpath is repo-only. Audit a live contract by passing an explicit<chain>:0x<addr>selector. - Optional ledger first. If an
-
MODE=onchain-> resolve the chain to an Etherscan V2 chain id and normalize the address, then fetch verified source in §S2:ADDR="${REPO##*:}" # after last ':' (or the whole string if none) CHAIN="${REPO%:*}"; [ "$CHAIN" = "$REPO" ] && CHAIN="eth" # before ':' or default eth CHAIN=$(printf '%s' "$CHAIN" | tr 'A-Z' 'a-z') ADDR=$(printf '%s' "$ADDR" | tr 'A-Z' 'a-z') # lowercase; explorer/Sourcify are checksum-insensitive case "$CHAIN" in eth|ethereum|mainnet) CID=1 ;; base) CID=8453 ;; arbitrum|arb) CID=42161 ;; optimism|op) CID=10 ;; polygon|matic) CID=137 ;; bsc|bnb) CID=56 ;; avalanche|avax) CID=43114 ;; gnosis|xdai) CID=100 ;; scroll) CID=534352 ;; linea) CID=59144 ;; zksync) CID=324 ;; blast) CID=81457 ;; sepolia) CID=11155111 ;; base-sepolia) CID=84532 ;; *) echo "unknown-chain: $CHAIN (add its Etherscan V2 chainid to S1 to support it)"; exit 0 ;; esac echo "onchain target: chain=$CHAIN cid=$CID addr=$ADDR"For a Blockscout-only chain not on Etherscan V2, add its
BLOCKSCOUThost in §S2b and the Etherscan calls no-op cleanly (verified source comes from the Sourcify/Blockscout fallback).On-chain findings are operator-gated - a live contract holding funds is the highest-stakes disclosure, so this mode NEVER auto-files a PVR, auto-sends an email, or opens any public channel; it stages an operator-gated draft and notifies (see §S7).
Dedup (mandatory in MODE=repo and MODE=onchain, same ledger as vuln-scanner). Before auditing, skip a target already covered in the last 30 days: read memory/vuln-scanned.json and skip any row inside the window - keyed on $REPO for a repo, on onchain:$CHAIN:$ADDR for an address. For a repo, also check gh api /repos/$REPO/security-advisories - a repo with a published/credited advisory for the same finding class is already handled; skip and log. This is the identical dedup contract described in vuln-scanner §A1 / §A6. MODE=fixture bypasses dedup (re-runnable).
S2. Get the code (fork a repo, copy a fixture, or fetch on-chain source)
Capture $WORKDIR first so every write lands in the real repo, not the throwaway target. Every mode works inside gitignored .scan/, so the build artifacts (out/, cache/, crytic-export/, fuzz corpus/) never touch the tracked tree.
WORKDIR="$(git rev-parse --show-toplevel)" # aeon repo root - memory/ and state live here
mkdir -p "$WORKDIR/.scan"
if [ "$MODE" = fixture ]; then
# Local regression: copy the bundled fixture into gitignored .scan/ and audit the COPY
# (never the tracked fixture) so forge's out/cache stay out of the working tree. No fork.
SRC="$WORKDIR/skills/sc-audit/fixtures/$FIXTURE"
[ -d "$SRC" ] || { echo "no-fixture: $FIXTURE"; exit 0; }
rm -rf "$WORKDIR/.scan/$FIXTURE"; cp -r "$SRC" "$WORKDIR/.scan/$FIXTURE"
# If the sandbox refuses `cp`, replicate the fixture files with the Read/Write tools instead
# and verify byte-identical with `diff -r "$SRC" "$WORKDIR/.scan/$FIXTURE"`.
cd "$WORKDIR/.scan/$FIXTURE"
elif [ "$MODE" = onchain ]; then
# Live contract by address: fetch the VERIFIED source from the explorer/Sourcify and
# materialize it as a Foundry project under .scan/. See §S2b for the fetch + materialize +
# on-chain-context steps; it lands you in the project dir. If no verified source exists,
# §S2b exits clean (bytecode-only audit is out of scope).
PROJ="$WORKDIR/.scan/onchain-$CHAIN-$ADDR"
echo "onchain project dir: $PROJ (materialize per §S2b, then cd there)"
# >>> run §S2b here <<< - after it, you are in "$PROJ" with src/ + foundry.toml written.
else
cd "$WORKDIR/.scan"
gh repo fork "$REPO" --clone --default-branch-only -- --depth 50 --quiet
cd "$(basename "$REPO")" # now in <workdir>/.scan/<repo>
fi
# --- Scratch dir for this run's intermediate files (scan JSON, sources.txt, fuzz harness).
# Prefer /tmp; fall back to a gitignored dir beside the clone under .scan/ when the skill
# sandbox blocks /tmp. RE-RUN these three lines at the top of every later Bash block that
# touches scratch (S4, S6.5) - claude -p spawns a FRESH shell per Bash call, so $SCRATCH does
# NOT persist (cwd does, shell vars don't). `$(cd .. && pwd)` is .scan/ (you're in .scan/<target>).
SCRATCH=/tmp/sc-audit
mkdir -p "$SCRATCH" 2>/dev/null && [ -w "$SCRATCH" ] || SCRATCH="$(cd .. && pwd)/_sc-audit"
mkdir -p "$SCRATCH"; echo "scratch: $SCRATCH"
# Confirm Solidity actually present (auto-select already filtered, but a direct $REPO may not have):
if ! ls **/*.sol >/dev/null 2>&1 && [ -z "$(find . -name '*.sol' -not -path '*/node_modules/*' 2>/dev/null | head -1)" ]; then
echo "no-solidity after clone: $REPO" # log a clean no-op row in S8 and exit
fi
S2b. On-chain source fetch, materialize, and context (MODE=onchain only)
Run this only when MODE=onchain. It fetches the contract's verified source, writes it as a Foundry project under $PROJ, and records on-chain context that drives severity. If no verified source exists, log it and exit clean - a bytecode-only audit is out of scope (decompilation is unreliable and would produce unfalsifiable findings; the honest output is "source not verified, cannot audit").
1. Fetch the verified source. Prefer Etherscan V2 (one key, ~60 chains, best coverage); fall back to Sourcify (keyless), then to Blockscout's keyless REST for Blockscout-explorer chains that are on neither. The Etherscan key goes through ./secretcurl as a {ETHERSCAN_API_KEY} placeholder (it ends _KEY, so it substitutes) - never put the raw key on the command line. Presence-check the key with ${VAR:+x}, not a bare $VAR (a bare secret expansion is blocked by the Bash layer):
SCRATCH=/tmp/sc-audit; mkdir -p "$SCRATCH" 2>/dev/null && [ -w "$SCRATCH" ] || SCRATCH="$WORKDIR/.scan/_sc-audit"; mkdir -p "$SCRATCH"
# Blockscout base URL for chains NOT on Etherscan V2 (keyless REST). One line per chain.
case "$CHAIN" in
*) BLOCKSCOUT="" ;;
esac
VERIFIED=no
# (a) Etherscan V2 getsourcecode - only if a key is configured
if [ -n "${ETHERSCAN_API_KEY:+x}" ]; then
./secretcurl -s -w 'http=%{http_code}\n' -o "$SCRATCH/etherscan.json" \
"https://api.etherscan.io/v2/api?chainid=$CID&module=contract&action=getsourcecode&address=$ADDR&apikey={ETHERSCAN_API_KEY}"
# verified iff result[0].ABI is real source (NOT the literal "Contract source code not verified")
if python3 - "$SCRATCH/etherscan.json" <<'PY'
import json,sys
try: r=json.load(open(sys.argv[1]))["result"][0]
except Exception: sys.exit(1)
sys.exit(0 if r.get("ABI","").strip() and r["ABI"]!="Contract source code not verified" and r.get("SourceCode","").strip() else 1)
PY
then VERIFIED=etherscan; fi
fi
# (b) Sourcify keyless fallback (no key, or Etherscan had no verified source).
# Use API **v2**. The old v1 route (/server/files/any/$CID/$ADDR) is deprecated; do not fall
# back to it. v2 returns ONE object (not a file list) whose `sources` maps path -> {content},
# so it needs its own parse branch below.
if [ "$VERIFIED" = no ]; then
curl -s -o "$SCRATCH/sourcify.json" \
"https://sourcify.dev/server/v2/contract/$CID/$ADDR?fields=sources,compilation,proxyResolution,deployment" \
2>/dev/null || true
grep -q '"sources"' "$SCRATCH/sourcify.json" 2>/dev/null && VERIFIED=sourcify || true
fi
# (c) Blockscout v2 fallback - Blockscout-explorer chains that are on NEITHER Etherscan V2 nor
# Sourcify. Keyless GET /api/v2/smart-contracts/<addr> returns ONE object: source_code + file_path
# + additional_sources[{file_path,source_code}] + proxy_type + implementations
# + compiler_version/evm_version. Parsed by its own branch in step 2.
if [ "$VERIFIED" = no ] && [ -n "$BLOCKSCOUT" ]; then
curl -s -o "$SCRATCH/blockscout.json" "$BLOCKSCOUT/api/v2/smart-contracts/$ADDR" 2>/dev/null || true
if python3 - "$SCRATCH/blockscout.json" <<'PY'
import json,sys
try: d=json.load(open(sys.argv[1]))
except Exception: sys.exit(1)
sys.exit(0 if d.get("is_verified") and (d.get("source_code") or "").strip() else 1)
PY
then VERIFIED=blockscout; fi
fi
echo "verified-source: $VERIFIED"
if [ "$VERIFIED" = no ]; then
echo "onchain: source NOT verified for $CHAIN:$ADDR - cannot audit (bytecode-only out of scope)."
# Write a clean dedup row (channel: skipped) + coverage note in S8, notify nothing, exit.
fi
2. Materialize the Foundry project at $PROJ (src/ + a synthesized foundry.toml). Etherscan's SourceCode field has three shapes - a plain flattened string, a single-brace { "File.sol": {"content": ...} } map, or a double-brace {{ ...standard-json... }} object - so parse with Python, not jq. This snippet handles all three plus the Sourcify file list, and pins solc/evm_version from the compiler metadata:
python3 - "$SCRATCH" "$PROJ" "$VERIFIED" <<'PY'
import json, os, re, sys
scratch, proj, src_from = sys.argv[1], sys.argv[2], sys.argv[3]
os.makedirs(os.path.join(proj, "src"), exist_ok=True)
def write(relpath, content):
p = os.path.join(proj, "src", relpath.lstrip("/"))
os.makedirs(os.path.dirname(p) or os.path.join(proj, "src"), exist_ok=True)
open(p, "w").write(content)
solc = evm = name = ""; proxy = "0"; impl = ""
if src_from == "etherscan":
r = json.load(open(os.path.join(scratch, "etherscan.json")))["result"][0]
name = r.get("ContractName") or "Contract"; proxy = str(r.get("Proxy", "0")); impl = r.get("Implementation", "")
m = re.search(r"v?(\d+\.\d+\.\d+)", r.get("CompilerVersion", "")); solc = m.group(1) if m else ""
ev = (r.get("EVMVersion") or "").strip().lower(); evm = "" if ev in ("", "default") else ev
s = (r.get("SourceCode") or "").strip()
if s.startswith("{{") and s.endswith("}}"):
for path, v in json.loads(s[1:-1]).get("sources", {}).items(): write(path, v.get("content", ""))
elif s.startswith("{"):
obj = json.loads(s)
srcs = obj.get("sources", obj)
for path, v in srcs.items():
if isinstance(v, dict) and "content" in v: write(path, v["content"])
else:
write(f"{name}.sol", s)
elif src_from == "sourcify": # sourcify API v2 - ONE object: sources{path:{content}} + compilation + proxyResolution
d = json.load(open(os.path.join(scratch, "sourcify.json")))
for path, v in (d.get("sources") or {}).items():
write(path, v.get("content", "") if isinstance(v, dict) else v)
comp = d.get("compilation") or {}
name = comp.get("name") or "Contract"
m = re.search(r"(\d+\.\d+\.\d+)", comp.get("compilerVersion", "")); solc = m.group(1) if m else solc
ev = ((comp.get("compilerSettings") or {}).get("evmVersion") or "").strip().lower()
evm = "" if ev in ("", "default") else ev
pres = d.get("proxyResolution") or {}
if pres.get("isProxy"):
proxy = "1"
impls = pres.get("implementations") or []
# v2 implementation entries are dicts ({"address": ...}) or bare strings
if impls: impl = impls[0].get("address", "") if isinstance(impls[0], dict) else impls[0]
else: # blockscout v2 - /api/v2/smart-contracts: source_code + file_path + additional_sources[]
d = json.load(open(os.path.join(scratch, "blockscout.json")))
if d.get("file_path"): write(d["file_path"], d.get("source_code") or "")
else: write(f'{d.get("name") or "Contract"}.sol', d.get("source_code") or "")
for a in (d.get("additional_sources") or []):
if a.get("file_path"): write(a["file_path"], a.get("source_code") or "")
name = d.get("name") or "Contract"
m = re.search(r"(\d+\.\d+\.\d+)", d.get("compiler_version", "")); solc = m.group(1) if m else solc
ev = (d.get("evm_version") or "").strip().lower(); evm = "" if ev in ("", "default") else ev
pt = d.get("proxy_type")
if pt and str(pt).lower() not in ("none", "unverified"):
proxy = "1"
impls = d.get("implementations") or []
if impls: impl = impls[0].get("address", "") if isinstance(impls[0], dict) else impls[0]
ft = ['[profile.default]', 'src = "src"', 'out = "out"', 'libs = ["lib"]']
if solc: ft.append(f'solc = "{solc}"')
if evm and evm != "default": ft.append(f'evm_version = "{evm}"')
open(os.path.join(proj, "foundry.toml"), "w").write("\n".join(ft) + "\n")
json.dump({"name": name, "solc": solc, "evm": evm, "proxy": proxy, "implementation": impl},
open(os.path.join(scratch, "materialized.json"), "w"))
print(f"materialized: name={name} solc={solc} evm={evm} proxy={proxy} impl={impl}")
PY
cd "$PROJ"
3. If it's a proxy, also materialize the implementation - the proxy shell holds almost no logic; the bug is in the implementation it delegatecalls. When materialized.json has proxy=1 and a non-empty implementation, re-run steps 1-2 for that implementation address (fetch impl on the same $CID, write it under src/impl/) so S4/S5 audit the real logic. Note the proxy->impl relationship prominently in the report. If the explorer didn't flag a proxy, still probe the EIP-1967 implementation slot before concluding it's non-upgradeable (step 4).
4. Record on-chain context (drives severity - a live contract holding funds turns a "possible" into "exploitable now"). Best-effort via the Etherscan V2 proxy/account modules (same key), Blockscout's keyless Etherscan-compatible /api for Blockscout chains, or a public RPC; each is optional, never fatal. Write $SCRATCH/onchain-context.json:
if [ -n "$BLOCKSCOUT" ]; then
# Blockscout chains: Etherscan V2 does not cover $CID, so read the native balance from
# Blockscout's keyless Etherscan-compatible /api. Its result is a wei string in the SAME
# {"status","message","result"} shape as Etherscan V2's balance response, so it parses
# identically into native_balance_wei. Proxy/impl already came from the materialize step.
# An optional BLOCKSCOUT_API_KEY is passed via secretcurl's {BLOCKSCOUT_API_KEY} placeholder -
# the legacy /api honors ?apikey= on key-enforcing instances; instances that don't enforce
# keyed tiers ignore it. Keyless otherwise. The v2 source endpoint above ignores the key.
if [ -n "${BLOCKSCOUT_API_KEY:+x}" ]; then
./secretcurl -s -o "$SCRATCH/balance.json" \
"$BLOCKSCOUT/api?module=account&action=balance&address=$ADDR&tag=latest&apikey={BLOCKSCOUT_API_KEY}"
else
curl -s -o "$SCRATCH/balance.json" \
"$BLOCKSCOUT/api?module=account&action=balance&address=$ADDR&tag=latest" 2>/dev/null || true
fi
elif [ -n "${ETHERSCAN_API_KEY:+x}" ]; then
# native balance at the address = funds directly at risk (wei)
./secretcurl -s -o "$SCRATCH/balance.json" \
"https://api.etherscan.io/v2/api?chainid=$CID&module=account&action=balance&address=$ADDR&tag=latest&apikey={ETHERSCAN_API_KEY}"
# EIP-1967 implementation slot (proxy detection independent of the explorer flag)
./secretcurl -s -o "$SCRATCH/eip1967.json" \
"https://api.etherscan.io/v2/api?chainid=$CID&module=proxy&action=eth_getStorageAt&address=$ADDR&position=0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc&tag=latest&apikey={ETHERSCAN_API_KEY}"
fi
Capture into $SCRATCH/onchain-context.json: {chain, cid, address, name, solc, evm_version, is_proxy, implementation, native_balance_wei, admin_or_owner (best-effort), notes}. Also note the ERC20/TVL exposure qualitatively if it's obvious from the contract type (a router/vault/bridge holding user funds is high-exposure). This context is non-sensitive (it does not aid exploitation) and rides into the S9 report and the staged disclosure so the operator sees the blast radius. From here, S3-S6.5 run unchanged against $PROJ - the materialized source is just another Foundry project in .scan/.
S3. Stage the toolchain and harden the build
slither, solc, solc-select, forge, crytic-compile, echidna, medusa are the contract toolchain. Every step self-guards with command -v, so any tool that is not present is skipped, never fatal - the source pass (S5) is the reliable core and needs none of them. Staging splits two ways:
- In-run (pip-installable):
slither-analyzer,solc-select(downloadssolc), andcrytic-compileinstall via the allow-listedpython3 -m pip. This covers Slither on hardhat (vianpx) and bare-solc layouts. - Workflow-staged (binaries):
forge,echidna,medusaship as tarballs/binaries whose installers need shell tooling that may not be allow-listed in-run, so they are only available when the instance provides a pre-claude -pstaging step that installs them and appends their dir to$GITHUB_PATH. When no such step ran or a tool failed to stage: a foundry repo may not build (Slither degrades tocompile-fail, source pass S5 still runs), and the fuzz arm (S6.5) is skipped cleanly.
Harden first - you are about to compile and (in S6.5) fuzz UNTRUSTED contract code. Compiling and running a foundry test executes the target repo's build and test code in this runner. Disable foundry FFI so a malicious foundry.toml/test can't shell out to the host, and only ever work inside the throwaway .scan/<repo> clone.
export FOUNDRY_FFI=false # block forge tests from shelling out to the host
export PATH="/tmp/bin:$HOME/.local/bin:/usr/local/bin:$HOME/.foundry/bin:$PATH"
# In-run pip tools (crytic-compile drives the build for slither/echidna/medusa):
command -v slither >/dev/null 2>&1 || python3 -m pip install --quiet --disable-pip-version-check slither-analyzer 2>/dev/null || true
command -v solc-select >/dev/null 2>&1 || python3 -m pip install --quiet --disable-pip-version-check solc-select 2>/dev/null || true
command -v crytic-compile >/dev/null 2>&1 || python3 -m pip install --quiet --disable-pip-version-check crytic-compile 2>/dev/null || true
# forge/echidna/medusa: use them only if a workflow step already staged them (command -v guards below).
Pick the contract's solc version when a compile fails on version mismatch: read the pragma solidity line and solc-select install <ver> && solc-select use <ver> (solc-select downloads the binary via Python - this works in-run). crytic-compile auto-detects hardhat (npx hardhat), foundry (forge, if staged), and bare-solc layouts.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 750
- Forks
- 264
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
sc-audit- Source
- github.com/aeonfun/aeon