// Landing page — getfossyl.dev function Landing() { const { go } = useNav(); const { authed } = useAuth(); const { toast } = useToast(); const featuresRef = useRef(null); const howRef = useRef(null); const pricingRef = useRef(null); const scrollTo = (ref) => ref.current && ref.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); const ctaPrimary = () => go(authed ? 'upload' : 'signup'); const onComingSoon = (label) => toast(`${label} — coming soon`); return (
go('signup')} onLogin={() => go('login')} onProduct={() => scrollTo(featuresRef)} onHow={() => scrollTo(howRef)} onPricing={() => go('pricing')} onChangelog={() => onComingSoon('Changelog')} onDocs={() => onComingSoon('Docs')} authed={authed} /> go('report')} authed={authed} />
go('report')} />
go('pricing')} />
); } function TopNav({ onCta, onLogin, onProduct, onHow, onPricing, onChangelog, onDocs, authed }) { const { go } = useNav(); return (
go('landing')} style={{ cursor: 'pointer' }}>
{authed ? : <> }
); } function Hero({ onCta, onSample, authed }) { return (
{/* faint grid */}
Fossyl — Field report v1.2 · Stratum 2005—2015 / Zope · Plone · ZODB

Turn the legacy codebase
you inherited into a map
you can actually read.

Fossyl ingests a Zope/Plone codebase — filesystem, live ZODB export, optional MySQL schema — and produces a structured archaeological report of what runs in production, what was never version-controlled, what breaks if you touch it, and what is safe to remove.

Upload ZIP + ZODB export. Full report in ≤ 60s. No data retained after download.
{/* Stratum ribbon */}
Specimen 01 — Charité Berlin / Patient Portal (ca. 2008) scan_id F-8a3f · 2026-04-12 09:14
); } function HeroStratum({ label, count, sub, tone, bar }) { const colorMap = { slate: 'var(--slate)', bone: 'var(--bone)', rust: 'var(--rust)', lichen: 'var(--lichen)', line: 'var(--line-2)' }; return (
{label}
{count}
{sub}
{bar.map(([c, pct], i) => (
))}
); } function KeyFigures() { return (
); } function ProblemStatement() { return (
§ 01 — The problem

Half of what's running
isn't in the repo.

Zope/Plone systems allow code to live in two places simultaneously — the filesystem (visible, version-controlled) and the live ZODB object database (invisible, untracked, often the only copy).

A script edited through the browser five years ago by a contractor who has since left the organisation exists nowhere except the running server. If that server dies, the code is gone. If you're planning a migration, you cannot plan around what you cannot see.

Fossyl makes the invisible layer visible.{' '} It reconciles the filesystem against the live database, surfaces every script that has no version-controlled copy, and ranks each artifact by how much downstream code depends on it.

A Patient-Admission workflow in Specimen 01 contained 14 TTW scripts. None were in git. Three of them wrote directly to the billing ledger. Of 862 ZODB records examined, 693 (80.4%) had no counterpart on the filesystem — the definition of the object existed only in the running process. The oldest divergence we have measured: a Python Script named sendReferralFax last modified 2011-03-08. Still in the critical path in 2026.
); } function HowItWorks() { const steps = [ { n: '01', t: 'Ingest', d: 'Upload your repository ZIP and a ZODB export. Optionally add a phpMyAdmin SQL dump for schema mapping.', meta: 'zip · fs.data · schema.sql' }, { n: '02', t: 'Excavate', d: 'Fossyl parses every ZODB record, walks the filesystem, resolves Zope traversal, and reconciles the two layers. 40–60 seconds on a mid-sized codebase.', meta: '~52s median · 862 records' }, { n: '03', t: 'Map', d: 'The dependency graph, divergence matrix, coupling analysis, and version-group detection run on the reconciled tree.', meta: '8 sections · 412 edges' }, { n: '04', t: 'Read', d: 'A self-contained HTML report is generated — readable in a browser, printable as PDF, no server required after download.', meta: 'single file · ≤ 4.2 MB' }, ]; return (
§ 02 — Method

Four layers. One report.

Fig. 02 — Ingestion pipeline. Each layer can be audited independently.
{steps.map((s, i) => (
{s.n} {s.meta}

{s.t}

{s.d}

))}
); } function ReportPreview({ onOpen }) { return (
§ 03 — The report

Eight sections.
One HTML file.

Fig. 03 — §2 Risk stratigraphy (excerpt from a real report)
synchronized diverged TTW-only
); } function ReportListItem({ num, title, meta }) { return (
{num} {title}
{meta}
); } function FeaturesGrid() { const items = [ { e: '§ Divergence', t: 'Filesystem vs live database', d: 'Every script running in production, mapped against what exists in version control. The delta is the danger.' }, { e: '§ Graph', t: '400+ edge dependency map', d: 'Directed graph of which scripts call which, with adjustable layout (force, radial, adjacency matrix).' }, { e: '§ Coupling', t: 'Risk ranking', d: 'Identifies the files with the most downstream dependents — the ones that break everything if you touch them.' }, { e: '§ Version', t: 'Version-group detection', d: 'Finds duplicate or near-duplicate logic across the codebase. Three copies of the same function is three bugs waiting.' }, { e: '§ Dead', t: 'Unreferenced artifacts', d: 'Candidates for deletion. Scripts, templates and objects with zero callers, still taking up space in the ZODB.' }, { e: '§ AI', t: 'Plain-English field notes', d: 'For each critical artifact, a dry, factual explanation of what it does and why changing it is dangerous.' }, { e: '§ Source', t: 'Full source browser', d: 'Every file, filterable and searchable, inside the same self-contained HTML report.' }, { e: '§ Schema', t: 'Database schema map', d: 'Optional. Ingest a phpMyAdmin SQL export and get the relational layer cross-referenced to Zope types.' }, ]; return (
§ 04 — What you get

Every section earns its place.

{items.map((it, i) => (
{it.e}

{it.t}

{it.d}

))}
); } function PricingStrip({ onCta }) { return (
§ 05 — Pricing

One scan. One report.
One flat fee.

Most customers buy a single scan to prepare a migration. Teams running multiple sites subscribe. Consulting firms use the Agency plan.

); } function PricingMini({ name, price, sub, featured }) { return (
{name} {sub}
{price}
); } function TestimonialStrip() { return (
§ 06 — Specimen reports

"We had spent four months arguing about what the old patient portal actually did. Fossyl answered the argument in 52 seconds. 693 scripts that weren't in git. We had estimated 40."

— Engineering lead, University hospital (name withheld). Specimen 01.
); } function FAQSection() { const faqs = [ { q: 'Where does my code go?', a: 'Uploads are processed in an ephemeral container and deleted immediately after the report is generated. Fossyl retains no copy of your source, your ZODB, or your schema.' }, { q: 'What about the AI explanations — is my code sent to a third party?', a: 'Only the signatures and call sites of critical artifacts are sent to the model, never the full source. You can disable AI explanations entirely; the rest of the report is generated locally.' }, { q: 'Can I run Fossyl on-premise?', a: 'Yes. Enterprise plans include a Docker image that runs inside your network. Nothing leaves your infrastructure.' }, { q: 'What frameworks does it support?', a: 'Today: Zope 2, Zope 4, Plone 4/5/6. The ingestion layer is modular. Django, Rails and legacy Java EE are on the roadmap.' }, { q: 'What about very large codebases?', a: 'Tested up to 50k files / 12k ZODB records. The report stays under 12 MB and remains a single HTML file.' }, ]; return (
§ 07 — Questions from the field

Frequently asked.

{faqs.map((f, i) => (
Q.0{i+1}

{f.q}

{f.a}

))}
); } function Footer() { const { go } = useNav(); const { toast } = useToast(); const cs = (label) => () => toast(`${label} \u2014 coming soon`); const scrollToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' }); const product = [ ['Features', () => { document.querySelector('[data-section="features"]')?.scrollIntoView({ behavior: 'smooth' }); }], ['Pricing', () => go('pricing')], ['Changelog', cs('Changelog')], ['Sample report', () => go('report')], ['Roadmap', cs('Roadmap')], ]; const resources = [ ['Docs', cs('Docs')], ['ZODB primer', cs('ZODB primer')], ['Plone 6 migration', cs('Migration guide')], ['Support', () => { window.location.href = 'mailto:support@getfossyl.dev'; }], ['Status', cs('Status page')], ]; const company = [ ['About', scrollToTop], ['Contact', () => { window.location.href = 'mailto:eri@getfossyl.dev'; }], ['Privacy', cs('Privacy policy')], ['Terms', cs('Terms of service')], ['Security', cs('Security')], ]; return ( ); } function FooterCol({ title, items }) { return (
{title}
{items.map(([label, onClick]) => ( {label} ))}
); } Object.assign(window, { Landing });