A Web3 wallet and agent platform for the Unicity network - crypto wallet, DMs, group chat, and marketplace.
This report presents the forensic synthetic code analysis of unicity-sphere/sphere, a TypeScript project with 9,727 GitHub stars. SynthScan v2.0 examined 72,376 lines of code across 450 source files, recording 147 pattern matches distributed across 7 syntactic categories. The overall adjusted score of 3.8 places this repository in the Likely human-written band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
This chart maps the temporal evolution of the adjusted synthetic code score across successive scan runs. An upward trajectory indicates ongoing incorporation of AI-generated code or expanding LLM-assisted scaffolding; a stable or declining trajectory may reflect active human refactoring, code removal, or the adoption of stricter authorship policies. The dashed secondary line (right axis) independently tracks total raw pattern hit count, which can diverge from the normalised score when codebase size changes significantly between scans.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 147 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | run-sphere.sh | 17 | # ── App identity ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | run-sphere.sh | 22 | # ── App networking ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | run-sphere.sh | 31 | # ── Source ssl-manager run library ─────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | run-sphere.sh | 41 | # ── App hooks ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 46 | # ── Fail-closed (#351) ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 71 | # ── Subscription flag sanity ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 84 | # ── AGGREGATOR_API_KEY requirement (conditional on subscriptions) ───────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 101 | # ── Build the substitution program ─────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 116 | # ── Runtime config global (window.__SPHERE_RUNTIME_CONFIG__) ──────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 158 | # ── Apply over the built JS (one sed program, all files) ───────────────────── | COMMENT |
| MEDIUM | deploy/runtime-config.sh | 165 | # ── Content-Security-Policy ────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 4 | # ── Export env so ssl-setup inherits our defaults ──────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 8 | # ── Normalize SSL_REQUIRED (fail closed on unknown values) ─────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 23 | # ── Validate env vars before use in nginx config ──────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 53 | # ── SSL setup (certs + HAProxy registration) ───────────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 90 | # ── Generate nginx config ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 181 | # ── Graceful shutdown ──────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 213 | # ── Runtime public config ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | deploy/entrypoint.sh | 220 | # ── Start nginx ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/e2e/two-profile-smoke.spec.ts | 229 | // ── Onboard both profiles (A funds itself via open minting) ─────────── | COMMENT |
| MEDIUM⚡ | tests/e2e/two-profile-smoke.spec.ts | 234 | // ── A reloads (F5): minted balance must survive the full pull ───────── | COMMENT |
| MEDIUM⚡ | tests/e2e/two-profile-smoke.spec.ts | 237 | // ── A sends 10 UCT to @B (mailbox delivery + B's claim/handoff) ─────── | COMMENT |
| MEDIUM | tests/e2e/two-profile-smoke.spec.ts | 254 | // ── B reloads (F5): received balance must survive the full pull ─────── | COMMENT |
| MEDIUM | tests/e2e/two-profile-smoke.spec.ts | 257 | // ── B requests 5 UCT from @A (§16 payment request) ──────────────────── | COMMENT |
| MEDIUM | tests/e2e/two-profile-smoke.spec.ts | 272 | // ── A pays it (payPaymentRequest: send + linked 'paid' respond) ─────── | COMMENT |
| MEDIUM | tests/e2e/two-profile-smoke.spec.ts | 278 | // ── Convergence: B holds 15 UCT, A holds 85 ─────────────────────────── | COMMENT |
| MEDIUM | tests/e2e/perf-many-tokens.spec.ts | 93 | // ── onboard ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/e2e/perf-many-tokens.spec.ts | 135 | // ── top up N times: each mint is one more UCT source token ──────────────── | COMMENT |
| MEDIUM | tests/e2e/perf-many-tokens.spec.ts | 145 | // ── send the whole balance, which must consume every token ──────────────── | COMMENT |
| MEDIUM | public/boot.js | 18 | // ── GitHub Pages SPA path restore ──────────────────────────────────────── | COMMENT |
| MEDIUM | public/boot.js | 35 | // ── Theme, applied before first paint ──────────────────────────────────── | COMMENT |
| MEDIUM | src/components/desktop/DesktopShortcuts.tsx | 30 | // ── Sortable wrappers ────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/hooks/useInstalledProjects.ts | 19 | // ── Local (offline / unauthenticated) cache of slugs ────────────────── | COMMENT |
| MEDIUM | src/hooks/useInstalledProjects.ts | 54 | // ── Hook ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/hooks/useInstalledProjects.ts | 117 | // ── Mutations ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ExplorePage.tsx | 24 | // ─── Constants ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ExplorePage.tsx | 50 | // ─── Featured Carousel ──────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ExplorePage.tsx | 67 | // ─── Hero stat ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ExplorePage.tsx | 89 | // ─── ExplorePage ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ProjectPage.tsx | 22 | // ── Helpers ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ProjectPage.tsx | 31 | // ── Drag-scrollable media strip ────────────────────────────────────── | COMMENT |
| MEDIUM | src/pages/ProjectPage.tsx | 104 | // ── Lightbox with keyboard + thumbnails ─────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 14 | // ── Types ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 51 | // ── JWT storage ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 65 | // ── Sign-in ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 121 | // ── Authenticated fetch ─────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 151 | // ── Installed-apps endpoints ────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 187 | // ── Ratings: recommend / helpful voting / replies ──────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 226 | // ── Helpful voting on a specific rating ────────────────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 243 | // ── Replies (Telegram-style comments on a review) ──────────────────── | COMMENT |
| MEDIUM | src/services/userApi.ts | 295 | // ── Moderation: report content, appeal a hide ──────────────────────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 5 | // ── Types ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 88 | // ── Fetch helpers ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 108 | // ── API functions ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 159 | // ── Public project metrics (live user/install/completion counts) ────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 216 | // ── Public project ratings (Steam-style reviews) ────────────────────── | COMMENT |
| MEDIUM | src/services/marketplaceApi.ts | 250 | // ── Public reply thread for a single rating ─────────────────────────── | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | run-sphere.sh | 1 | #!/bin/bash | COMMENT |
| LOW | docker-compose.yml | 21 | # Subscriptions (SGW) ship dormant; set exactly "true" to turn on. | COMMENT |
| LOW | deploy/runtime-config.sh | 1 | #!/bin/sh | COMMENT |
| LOW | deploy/runtime-config.sh | 21 | # SPHERE_API_URL quest-api base (marketplace / user / maintenance) | COMMENT |
| LOW | deploy/runtime-config.sh | 161 | find "$WEBROOT" -type f -name '*.js' -exec sed -i -f "$SED_SCRIPT" {} \; | COMMENT |
| LOW | tests/unit/chat/DMChatSection.peerParam.test.tsx | 1 | import { describe, it, expect, vi, beforeEach } from 'vitest'; | COMMENT |
| LOW | tests/unit/chat/DMChatSection.peerParam.test.tsx | 21 | // and they belong here, against the component a user can actually hit. | COMMENT |
| LOW | tests/unit/components/swapModalRates.test.tsx | 1 | import { describe, it, expect, vi, beforeEach } from 'vitest'; | COMMENT |
| LOW | tests/unit/components/installedProjectIconSdk.test.tsx | 61 | expect(openTab).toHaveBeenCalledWith('custom', { url: 'https://app.example.com', label: 'Agent Guild' }); | COMMENT |
| LOW | tests/unit/components/installedProjectIconSdk.test.tsx | 201 | }); | COMMENT |
| LOW | tests/unit/sdk/gatewayErrors.test.ts | 81 | COMMENT | |
| LOW | public/boot.js | 41 | // uses STORAGE_KEYS.THEME ('sphere_theme'), so the lookup always missed — and the | COMMENT |
| LOW | public/runtime-config.js | 1 | // Default runtime config for dev / GitHub Pages builds: empty, so values | COMMENT |
| LOW | .github/workflows/docker-build.yml | 41 | push: true | COMMENT |
| LOW | .github/workflows/deploy-pages-branch.yml | 21 | # just-merged deploy was silently cancelled and staging kept the old bundle. | COMMENT |
| LOW | .github/workflows/deploy-pages-branch.yml | 101 | run: npm run build | COMMENT |
| LOW | .github/workflows/deploy-pages-branch.yml | 121 | # Aggregator API key — inlined into the bundle at build time. | COMMENT |
| LOW | src/config/storageKeys.ts | 41 | // from identity via the SGW /auth flow). Cleared on wallet deletion. | COMMENT |
| LOW | src/utils/sentryScrub.ts | 21 | // 64+ hex chars: the shape of a raw private key (also matches sha256/txids — | COMMENT |
| LOW | src/utils/sentryScrub.ts | 121 | COMMENT | |
| LOW | src/components/chat/dm/DMChatSection.tsx | 61 | }); | COMMENT |
| LOW | src/components/marketplace/ProjectCard.tsx | 21 | // A chat agent is never installable — the API rejects an install call for | COMMENT |
| LOW | src/components/marketplace/ProjectCard.tsx | 41 | // mutually exclusive (both key off the same `type` field), so at most one | COMMENT |
| LOW | src/components/marketplace/ProjectReviewsSection.tsx | 81 | const [appealComment, setAppealComment] = useState(''); | COMMENT |
| LOW | src/components/agents/WalletRequiredBlocker.tsx | 21 | COMMENT | |
| LOW | src/components/agents/IframeAgent.tsx | 121 | const host = hostRef.current; | COMMENT |
| LOW | src/components/desktop/InstalledProjectIcon.tsx | 41 | setMenuOpen(false); | COMMENT |
| LOW | src/components/desktop/InstalledProjectIcon.tsx | 61 | // migration was written to retire, and do it from stale localStorage data | COMMENT |
| LOW | src/components/desktop/InstalledProjectIcon.tsx | 101 | // an entry that calls it — a non-https value gets no menu item at all, | COMMENT |
| LOW | src/components/desktop/DesktopLayout.tsx | 61 | navigate(`/agents/custom?url=${encodeURIComponent(url)}`); | COMMENT |
| LOW | src/components/wallet/L3/modals/SendModal.tsx | 121 | // Leaving confirm because the SEND started is the one exit that must NOT | COMMENT |
| LOW | src/components/wallet/L3/modals/SendModal.tsx | 141 | } | null>(null); | COMMENT |
| LOW | src/components/wallet/L3/modals/SendModal.tsx | 201 | // It is armed by a callback ref on every control that can authorize a spend, | COMMENT |
| LOW | …mponents/wallet/L3/hooks/useIncomingPaymentRequests.ts | 141 | const pay = useCallback(async (request: IncomingPaymentRequest) => { | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 181 | // (#449) so it doesn't have to re-click through the start screen. | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 221 | const [, setDetectedFileType] = useState<LegacyFileType>('unknown'); | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 521 | // never threaded into the new on-disk mnemonic (verified against the | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 761 | if (SUBSCRIPTION_ENABLED && active) { | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 861 | const handleBackupDownloadComplete = useCallback(() => { | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 881 | // In every case there is a real wallet on disk (with a token DB, etc.) by | COMMENT |
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 901 | // can encrypt the exported file with the SAME password — see | COMMENT |
| LOW | src/components/connect/ConnectIntentHandler.tsx | 101 | // THE INVARIANT (ConnectContext.armIntentShield): the §8.4 settle window | COMMENT |
| LOW | src/components/connect/ConnectIntentHandler.tsx | 141 | COMMENT | |
| LOW | src/sdk/SphereProvider.tsx | 241 | // its password this session — locked, not broken (#449). The SDK signals | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 261 | const passwordOpBusyRef = useRef(false); | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 321 | // toggle, unmount) destroys the instance it built instead of leaking it. See | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 341 | /** Rate-limits the persisted-unlock timestamp write; see the idle timer's onActivity. */ | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 381 | [], | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 601 | setInitProgress(null); | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 721 | // #449 no-wallet-loss guard: Sphere.init({autoGenerate:true}) only | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 841 | ); | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 981 | COMMENT | |
| LOW | src/sdk/SphereProvider.tsx | 1001 | try { | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 1021 | forEachConnectHost((host) => host.setLocked()); | COMMENT |
| LOW | src/sdk/SphereProvider.tsx | 1141 | localStorage.setItem(STORAGE_KEYS.AUTO_LOCK_TIMEOUT, encodeLockSettings(value, password)); | COMMENT |
| LOW | src/sdk/errors.ts | 61 | // The SINGLE source of truth for the send-pipeline error codes that mean the | COMMENT |
| LOW | src/sdk/hooks/payments/useTransfer.ts | 121 | // synthetic pending result below is returned in ALL cases, | COMMENT |
| LOW | src/sdk/hooks/payments/useTransfer.ts | 141 | // Swallow everything: annotation is best-effort, money-safety | COMMENT |
| LOW | src/sdk/hooks/core/useSphereEvents.ts | 261 | // - 'split:checkpoint-stuck' (sphere-sdk#501 / E.4): a certified split is | COMMENT |
| LOW | src/pages/ExplorePage.tsx | 101 | // the same status) never fire requests that would 503 during maintenance. | COMMENT |
| 6 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/unit/components/importPassword.test.tsx | 77 | async function driveRestoreToSetPassword() { | CODE |
| LOW | tests/unit/components/importPassword.test.tsx | 176 | async function selectAndImportEncryptedFile() { | CODE |
| LOW | src/sphere-sdk-browser.d.ts | 67 | export function createUnicityAggregatorProvider( | CODE |
| LOW | src/config/storageKeys.ts | 95 | export function getOrCreateWalletApiDeviceId(): string { | CODE |
| LOW | src/config/storageKeys.ts | 95 | export function getOrCreateWalletApiDeviceId(): string { | CODE |
| LOW | src/config/subscription.ts | 58 | function resolveSubscriptionApiUrl(): string { | CODE |
| LOW | src/components/chat/utils/groupChatHelpers.ts | 26 | export function getGroupFormattedLastMessageTime(group: GroupData): string { | CODE |
| LOW | src/components/chat/utils/groupChatHelpers.ts | 65 | export function getMessageSenderDisplayName(message: GroupMessageData): string { | CODE |
| LOW | src/components/layout/PendingTransfersIndicator.tsx | 41 | export function PendingTransfersIndicator() { | CODE |
| LOW | src/components/layout/WalletApiSessionIndicator.tsx | 18 | export function WalletApiSessionIndicator() { | CODE |
| LOW | …llet/onboarding/hooks/useOnboardingConnectionStatus.ts | 23 | export function useOnboardingConnectionStatus() { | CODE |
| LOW | src/components/connect/PaymentRequestIntentModal.tsx | 33 | export function PaymentRequestIntentModal({ | CODE |
| LOW | src/components/connect/rejectionMessage.ts | 38 | function describeProtocolRejection(data: Record<string, unknown>): string { | CODE |
| LOW | src/sdk/SphereProvider.tsx | 191 | function readCurrentAutoLockMinutes(oldPassword: string | null): AutoLockValue { | CODE |
| LOW | src/sdk/errors.ts | 143 | function isJsonRpcNetworkErrorShape( | CODE |
| LOW | src/sdk/hooks/payments/useTransfer.ts | 23 | function disambiguateGatewayAuthError(openUpgrade: (reason?: UpgradeReason) => void): void { | CODE |
| LOW | src/hooks/useKeyboardScrollIntoView.ts | 13 | export function useKeyboardScrollIntoView(_inputRef: RefObject<HTMLElement | null>) { | CODE |
| LOW | src/hooks/useMarketplace.ts | 188 | export function useProjectMetricsByGroups(groups: string[][]) { | CODE |
| LOW | src/services/announcementsClient.ts | 41 | export function createAnnouncementsClient(getSphere: () => Sphere | null): AnnouncementsClient { | CODE |
| LOW | src/services/telemetry.ts | 94 | export function getOrCreateClientId(): string { | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | run-sphere.sh | 5 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 657 | // Step 1: Check nametag availability via Nostr (no wallet needed) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …omponents/wallet/onboarding/hooks/useOnboardingFlow.ts | 657 | // Step 1: Check nametag availability via Nostr (no wallet needed) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/unit/utils/sentryScrub.test.ts | 51 | expect(scrubText('Auth failed for user@example.com')).not.toContain('user@example.com'); | CODE |