Skip to content

Publish the Argus site: Landing plus the Guide, synced from argusappsec/argus #1

Description

@davideimola

Problem Statement

Argus is public, pre-1.0, and already has roughly 1,200 lines of real user documentation — installation, the full argus.yaml reference, what it needs from a model, both channels, skills, Kubernetes hosting. None of it is readable as documentation. It is Markdown inside a Go repository, browsable only by someone who already found the repository and is willing to read raw files.

Someone who hears the name has nowhere to land. argusappsec.com is registered but serves only mail: no A record, no site. The pitch that would convince them exists, but only as a README.md on GitHub — reachable only by people who are already there.

Worse, the Guide has just been made unreadable in place on purpose. argus ADR 0022 converted it into self-contained Starlight source: internal links are now root-absolute site paths that 404 on GitHub, blockquote callouts became :::caution asides that render as literal text, and the folder's landing page became index.md, which GitHub does not render. The documentation was made site-ready for a site that does not exist. Until it does, the Guide reads worse than it did before.

Solution

A public site at argusappsec.com with two halves.

A Landing at / for someone who has never heard of Argus: it makes the Argument — deterministic scanners are precise but shallow, language models alone drown the signal, and an organization's own context is the missing third ingredient — and hands off to the Guide and the repository.

The Guide published under /guide/, with working navigation, working internal links, local search, light and dark themes, and Argus's own brand. It is not authored here. A Sync pulls it from argusappsec/argus on a schedule and replaces the Mirror, so the site tracks the Guide without anyone remembering to publish.

The site is a Publisher: it owns presentation, navigation, hosting, the design system and future Translations, and it owns no English prose at all. A typo seen on the site is fixed in argusappsec/argus.

User Stories

  1. As someone who has just heard the name Argus, I want a page that explains what it is in one screen, so that I can decide whether to keep reading without cloning a repository.
  2. As an AppSec lead evaluating tools, I want the Argument stated plainly — why scanners alone and models alone both fail — so that I can judge whether the premise matches the problem I have.
  3. As an AppSec lead, I want to see that Argus makes verifiable claims, so that a benchmark result carries more weight than adjectives.
  4. As a developer with fifteen minutes, I want the quick start visible from the Landing, so that I can try Argus before I finish evaluating it.
  5. As an operator who will host Argus, I want the Guide navigable with a sidebar, so that I can see the whole shape of what I have to run before I start.
  6. As an operator, I want internal links inside the Guide to work, so that the reading path from getting started to configuration to providers holds together.
  7. As an operator, I want to search the documentation, so that I can find a configuration key by name instead of reading the reference top to bottom.
  8. As an operator, I want search to work without an external service, so that the site has no dependency that can be rate-limited or go away.
  9. As an operator reading at night, I want a dark theme, so that the documentation is comfortable in the environment I actually work in.
  10. As a reader on a phone, I want the Guide and the Landing to be readable at that width, so that a link shared in a chat is usable.
  11. As a reader with low vision, I want text and links to meet WCAG AA contrast in both themes, so that the site is usable — and so that a security tool does not fail its own accessibility.
  12. As a developer whose pull request was reviewed by Argus, I want to reach an explanation of what the GitHub channel does, so that I understand what commented on my code.
  13. As someone who wants to run models locally, I want to learn that Argus speaks two protocols rather than supporting a fixed list of vendors, so that I know my own endpoint is a candidate.
  14. As a security-conscious evaluator, I want to learn that with a local model my code never leaves my infrastructure, so that I can consider Argus where sending code to a vendor is not allowed.
  15. As a maintainer of Argus, I want to edit documentation in argusappsec/argus next to the code it describes, so that a change to a default and a change to its documentation live in one pull request.
  16. As a maintainer of Argus, I want the published site to follow the Guide without a publishing step, so that shipping documentation costs nothing beyond merging it.
  17. As a maintainer of Argus, I want a page I delete upstream to disappear from the site, so that documentation for a removed feature does not stay online indefinitely.
  18. As a maintainer of Argus, I want a page I add upstream to appear on the site in the right position without anybody editing the site, so that new documentation cannot be silently omitted from navigation.
  19. As a maintainer of the site, I want the Mirror to be read-only by convention and by layout, so that nobody accidentally creates a second master for English prose.
  20. As a maintainer of the site, I want the Sync to run without any cross-repository credential, so that there is no secret to create, rotate, or leak.
  21. As a maintainer of the site, I want a broken Mirror to fail the build rather than publish, so that the last good deploy stays up instead of a 404 going live.
  22. As a maintainer of the site, I want an internal link that points at a slug which does not exist to fail the build, so that the rule argus ADR 0022 leaves unverified is enforced somewhere.
  23. As a maintainer of the site, I want a Token pair that violates its documented contrast ratio to fail the build, so that the measurements recorded in ADR 0004 stay true instead of being a one-time observation.
  24. As a maintainer of the site, I want to iterate on the Landing and see it deployed before it is public, so that design changes are reviewed by eye rather than in the abstract.
  25. As a maintainer of the site, I want the site to use the Argus brand colours, so that the site and the logo look like the same project.
  26. As a maintainer of the site, I want colour decisions expressed as roles rather than colour names, so that a component never has to know which theme is active.
  27. As a maintainer of the site, I want the Landing to link or read inventories rather than restate them, so that adding a fifth scanner upstream cannot turn the site into a liar.
  28. As a translator, I want my work to live outside the Mirror, so that the Sync can overwrite English at any time without touching a single translated word.
  29. As a translator, I want to see which English paragraphs changed, so that I know which parts of my translation went stale.
  30. As an agent working in argusappsec/argus, I want the site's requirements on the Guide to be discoverable in that repository, so that a new page arrives publishable without anyone remembering the conventions.
  31. As a person reporting a vulnerability in Argus, I want security@argusappsec.com to keep working after the site goes live, so that disclosure is never interrupted by a website launch.
  32. As a visitor who typed www, I want to arrive at the site, so that the domain works the way people type it.

Implementation Decisions

Five decisions were taken in a grilling session and recorded as ADRs in this repository. They are the frame for everything below and are not reopened here: ADR 0001 (this repository publishes the Guide; it does not own it), ADR 0002 (the Mirror is a committed copy, pulled on a schedule), ADR 0003 (Astro and Starlight, not Docusaurus), ADR 0004 (role-named Tokens over a hand-copied Palette), ADR 0005 (Cloudflare, served from the domain root).

Stack. Astro with Starlight, styled with Tailwind. Search is Starlight's bundled Pagefind: a local index, no external service, no application to file. Chosen over Docusaurus because Tailwind is a first-class Astro integration, search needs no setup, and the content root does not collide with the Agent docs already at the repository root.

The Mirror's location is fixed, not chosen. Starlight's docsLoader() hard-codes its base and does not expose it, so the Mirror must physically sit at src/content/docs/guide/. This is also what makes the URL prefix /guide/ rather than /docs/, which the Guide's own internal links are already written against. Any other mount point breaks every internal link in the Guide silently.

The Astro application lives at the repository root, not in a subdirectory. Astro reads only src/, public/ and its config, so the root docs/ directory holding Agent docs and this repository's ADRs is invisible to it. There is no collision to resolve.

Sync. A script, invoked by a scheduled workflow in this repository — not logic inside workflow YAML, because a workflow cannot be tested and a script can. It reads docs/guide/ from a public checkout of argusappsec/argus, replaces the Mirror wholesale, and commits directly to the default branch. It deletes as well as adds. No cross-repository credential exists: reading a public repository needs none, and writing here uses the workflow's built-in token. Empty diffs produce no commit.

The Sync is destructive over one directory whose only contents are mirrored pages. Future Translation directories are siblings of it, never interleaved, so destructive replacement is structurally unable to reach them — a guarantee of the layout rather than of an exclusion list that someone must remember to extend.

Navigation is split between the two repositories, deliberately. Ordering comes from sidebar.order in the Guide's frontmatter, so a new page appears in the right position with no edit here. Group labels must be set in this repository's Astro config: an autogenerated sidebar group takes its directory name verbatim, which would otherwise render channels and deployment lowercase. This is the second of three requirements argus ADR 0022 places on this repository.

Design system. One Tailwind @theme block is simultaneously the Token layer and the Tailwind theme; Starlight's own colour custom properties are mapped onto it, so the Tokens are the single source and both Tailwind and Starlight are consumers.

The five Palette values are transcribed by hand, each annotated with the brand colour it came from. Every other Token is derived from those five with color-mix(), so the five transcribed lines remain the only literal colour constants in the repository — the duplication accepted by decoupling the two repositories stays exactly five lines wide, and provenance is visible in code rather than in a comment.

Token roles, and their values per theme:

Token Light Dark
bg paper night
surface white derived from night
text night paper
text-muted derived derived
border derived derived
primary peacock teal verdigris
accent ocellus gold ocellus gold

primary changes value with the theme because it has to: apart from the two neutrals, no brand colour reads accessibly on both backgrounds. Peacock teal measures 5.68 on paper and 2.63 on night; verdigris measures 4.95 on night and 3.02 on paper. Role naming is a requirement of the Palette, not a stylistic preference. There is no secondary Token — verdigris is not a secondary colour, it is primary in dark.

Ocellus gold is a fill in light and a foreground in dark. It measures 1.76 on paper, unusable in the foreground; night on gold measures 8.5. So in light mode it fills — badge, callout, rule — with text over it. No darkened sixth colour is derived, because that colour would not be the brand's gold.

Brand assets are copied once, not synced: logo, light and dark banners, favicon. They change effectively never, and brand/ is deliberately outside the Sync's scope.

Landing. A plain Astro page at /, zero JavaScript by default. It may restate the Argument, because an argument does not go stale. It must never restate an Inventory — scanners, built-in skills, channels, quick-start commands, version numbers, model providers — and instead links into the Guide or reads from the Mirror.

The model story is framed as bring your own model, not supported models. The Guide states that a provider's type names a protocol rather than a vendor and that Argus certifies nobody's server; a "supported models" table would contradict the documentation it links to. Named runtimes appear as examples of servers that speak the protocol, and the claim that carries weight — with a local model, code never leaves the reader's infrastructure — is stated plainly.

Build-time guards, not fixers. Two checks run as part of the build and fail it rather than repairing anything: internal /guide/… links must resolve to slugs that exist, and Token pairs must meet the contrast ratios recorded in ADR 0004. A failing build leaves the previous deploy in place, so a bad Mirror degrades the site's freshness and never its correctness.

Hosting. Cloudflare, apex argusappsec.com, www redirecting to it. Serving from the domain root is the third requirement of argus ADR 0022: the Guide's internal links are root-absolute and Astro does not inject a base path into them, so a sub-path deployment breaks all of them. DNS is already on Cloudflare, previews are unlimited on the free plan, and there is no non-commercial clause — so a future sponsorship link on the Landing cannot change the hosting bill. Adding the site's DNS record leaves the existing MX records untouched, and security@argusappsec.com must keep resolving.

This repository must become public. The scheduled Sync is only free on a public repository, so this is part of the same decision rather than a separate step.

Testing Decisions

A good test here asserts externally observable behaviour and nothing else: that the build refuses bad input, that the Sync leaves the right files on disk. It never asserts how a check is implemented, which plugin performs it, or what a function is named internally — those change without the behaviour changing. There is no prior art in this repository: it is greenfield, with no package.json and no tests, so both seams are new and were chosen to be as few and as high as possible.

Seam 1 — the build. On a static site the build is the highest available seam: one command, red or green. Three classes of failure converge on it.

  • Invalid Mirror content — missing frontmatter, a broken content collection — already fails the build unaided.
  • An internal /guide/… link that resolves to no existing slug fails the build. This is this repository's half of the rule argus ADR 0022 leaves explicitly unenforced; the upstream half, a grep in that repository's CI, is out of scope here.
  • A Token pair that violates its documented ratio fails the build. This makes ADR 0004's contrast table executable rather than a measurement taken once. Only pairs carrying a WCAG claim are asserted; color-mix()-derived Tokens make no such claim and so have nothing to assert.

Seam 2 — the Sync script, invoked with a source and a destination directory and tested against fixture trees. This is the only behaviour the build cannot reach, because it happens before a build exists. Three assertions:

  • a page added upstream appears in the Mirror
  • a page deleted upstream disappears from the Mirror — the property that keeps documentation for a removed feature from staying online
  • a sibling directory, standing in for a future Translation, is left untouched — the structural guarantee the whole translation story rests on

Tests run on node:test from the Node standard library. Vitest is more idiomatic in an Astro project but adds a dependency that buys nothing at two test files.

Deliberately untested: the Argument/Inventory rule on the Landing, which is editorial discipline and not machine-checkable.

Out of Scope

  • Translations. No locale is added, and no i18n configuration ships. The layout must merely leave room for one as a sibling of the Mirror.
  • The locale-prefixing transform. The Guide's internal links are root-absolute and therefore not locale-aware: a translated page linking /guide/configuration/ would send its reader back to English. Recorded in ADR 0002 as a known, load-bearing requirement of the first Translation. Nothing is built for it now.
  • A review gate on the Sync. Direct commits, no pull request. The gate arrives with the first Translation, when the diff finally has a reader to serve.
  • Immediate propagation. Cron only. The upgrade path — a dispatch triggered from argus with a token scoped to starting workflows and nothing else — is recorded and deliberately not built.
  • Versioning. Nothing pre-1.0. At 1.0 this becomes a community Starlight plugin.
  • The grep guard in argusappsec/argus CI. Confirmed as separate work in that repository.
  • Updating argusappsec/argus's README. Its seven links still send the public to the Guide's raw source files, which now have broken internal links and literal :::caution blocks. They should point at the site once it is live. Work in the other repository.
  • Publishing the ADRs or design documents. Considered and rejected for now: they cross-reference each other and the domain glossary, and they are written for maintainers. Attractive later as positioning for a project whose differentiator is its reasoning.
  • A shared design-system repository. Reopens only if Argus ever ships a UI of its own.
  • Changing the Palette, or adding a brand typeface. The Palette is transcribed as it is; no sixth colour is invented and no typeface is chosen as part of this work.

Further Notes

Known failure modes, recorded so they are not discovered late. GitHub disables scheduled workflows after sixty days of repository inactivity, and a Sync that never finds a diff produces no activity — this is the quiet way the Sync dies, and there is no alarm for it. Renaming a Guide page now breaks a public URL rather than a relative link, because the site's URLs become the project's public contract.

Requirements received rather than decided. Three arrive from argus ADR 0022 and cannot be renegotiated here: mount the Mirror at src/content/docs/guide/, relabel the autogenerated sidebar groups, serve from the domain root. All three are satisfied by decisions above, and the third — which that ADR calls the one most likely to be discovered late — was settled by the hosting decision for an unrelated reason.

Verified facts behind the stack decision were researched against Starlight 0.41.5 and Astro 7.1.6 and recorded in argusappsec/argus under docs/research/starlight-content-conventions.md. That file is the reference for content conventions and for docsLoader()'s fixed base; it is worth reading before implementing the Mirror mount or the sidebar configuration.

Vocabulary. This repository's glossary is in CONTEXT.md. The word "docs" is ambiguous here by construction and should not be used unqualified: the Mirror is the read-only copy of the Guide, Agent docs is the root directory holding skill configuration and ADRs, and the Guide is the prose itself, which lives in another repository.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions