Security & deployment

Built for environments where the boundary is not negotiable.

Guidepost runs on your own network with no dependency on the public internet. This page covers how access control is layered, what the audit trail records, how content and uploads are validated, the discipline behind the test suite, and where the accessibility work has and has not reached. All of it applies equally to a hosted deployment — the platform is the same either way.

Deployment posture

Closed networks are the design target, not a supported exception.

Most platforms treat an air-gapped deployment as a configuration to be discovered. In practice that surfaces as fonts that never load, embeds that hang, and features that quietly do nothing.

No public internet at runtime

The application does not reach outward to operate. Nothing in normal use depends on a CDN, a font host, an analytics endpoint or an external API.

No external data repository

A self-managed deployment reaches nothing outside itself — no external store, no phone-home. Data stays inside the boundary you deploy into.

Nothing reaches out on its own

Directory sync, external student records sync and SCORM completion export exist in the codebase but have never been connected to or verified against a real external system. The AI assistant is likewise scaffolded and dormant. None of them runs or attempts a connection by default.

This describes the self-managed option. It is how Guidepost is built and it remains the right answer for a closed or air-gapped network. If you would rather not operate infrastructure, the same platform is available hosted by us, with your data in its own isolated environment. The access control, audit trail, sanitization and testing described on the rest of this page are properties of the application and apply to both.

Access control

Layered, and re-derived at the point of action.

Two checks, not one gate

Access control is layered: a role and placement check, plus a scope check re-derived at the point of action. A single gate either holds or does not; two independent checks mean an error in one does not silently become an exposure.

One rule for management scope

A single primitive answers "what can this person manage" for every level in the hierarchy — strictly below their own placement, never at their own level. That last part is deliberate: it prevents the case where a staff member can delete the assignment granting their own access.

Read visibility follows the same structure through the same shared helpers, rather than each page assembling its own idea of what a viewer should see.

Deliberate exclusions are recorded, not just enforced

The platform's full-access administrative role is a single, greppable grant with one deliberate exception: private messaging between course staff and the students they teach, on the grounds that correspondence is not an administrative surface.

The grant is declared in one place precisely so it is reversible, and the surviving exclusion is covered by tests that fail if it is silently removed.

Restricted courses

A per-course materials lock excludes even Organization, Schoolhouse and Group-level staff from a course's content, limiting access to staff specifically assigned to it. Broader administrative access does not reach past the lock.

Audit trail

Attributable and timestamped

  • Every enrollment, including who approved it and when
  • Every grade, and every regrade
  • Every certificate issued, independently verifiable by code
  • Every administrative action across the hierarchy
  • Version history on materials, videos, surveys and scheduled reports

Accounts

Provisioned, not self-served

Course-level and organizational accounts are created by an administrator. There is no public self-registration. Every account carries a unique numeric identifier (AIN) alongside name and email.

Content & upload safety

Everything authored, and everything uploaded, is checked.

Sanitization on every rich-text input

Authored content is sanitized as it is stored. No inline scripts are permitted anywhere in authored material.

Sandboxed embeds

Embedded content is constrained rather than trusted, and PDFs are rendered inline behind a clickjacking-safe viewer.

Uploads validated twice

Every PDF and Excel upload is validated by both file extension and content signature — never either alone. All three document types share one validation helper rather than three near-identical ones.

External links marked and hardened

Any link whose origin differs from the current page is automatically detected, marked, and hardened, with an escape hatch for internally proxied links.

Verification discipline

A passing test suite is not the same as protection.

Guidepost carries a large automated test suite — over 5,800 tests in the application, on a build lineage of more than 7,000 — run on every code change before it ships.

Beyond routine coverage, safety-critical logic is checked by mutation testing: deliberately reintroducing a bug to confirm the test suite actually catches it, rather than trusting that a passing suite implies real protection. This has caught several guards that initially passed against the exact defect they were written to detect.

A related discipline applies to access rules. Any view that assembles its own data set rather than going through a shared, rule-carrying helper is where the next hole will be — so those are swept for deliberately rather than found by accident.

A worked example

"Hidden" and "hidden for the right reason"

A fix suppressing progress bars on the Alumni page was verified by a test asserting that the percentage bar's marker was absent. The test passed — while the module bar, a separate element behind a separate guard, was still rendering.

The lesson is recorded in the test itself: "the element is hidden" and "the element is hidden for the reason I think" are different claims, and a test matching the wrong marker confirms the first while the second is false.

Accessibility

Built and tested against WCAG 2.1 AA, and clear about where that stops.

Accessibility is an active line of work on Guidepost rather than a sentence at the foot of a page. It is also a standard no tool can fully measure, and that is said here rather than left for you to find out during a review.

What is actually measured

A browser-driven scan covers twenty-two pages against exactly the WCAG 2.1 AA rule set — 2.0 A, 2.0 AA, 2.1 A and 2.1 AA — rather than a wider "best practice" set that mixes in rules which are not success criteria. The pages span a signed-out visitor, an authoring account and a real student session, and the learner pages are scanned separately because they are a genuinely different set of templates and the ones a customer's people actually live in.

The scan asserts strictly zero violations — not a budget, and not "no new ones". It runs against a populated deployment rather than an empty install, because an empty page has no badges, no tables and no course content, and would pass every rule while proving nothing. Every contrast failure found so far was on something that only exists once there is real data. A coverage floor is asserted alongside it, so a page that starts redirecting or returning a 404 makes the suite redder rather than greener.

It is run deliberately, and that has a cost

The scan needs a real browser, which the rest of the test suite does not, so it sits behind a switch and is run as part of accessibility work rather than on every commit with everything else. That is a considered trade and it is not free: a critical fault introduced by an unrelated feature — invalid ARIA attributes a guided-overlay library was putting on whatever it highlighted — sat live for two days before the next run found it. Nothing had regressed; the guard simply had not been run since the feature that broke it arrived.

It is recorded here because it is the honest shape of this work, and because a scan behind a switch is a scan that can quietly stop happening.

The scanner is pinned, not fetched

The accessibility engine is vendored into the codebase with its version in the filename, rather than loaded from a content delivery network when the tests run. A scanner fetched at runtime fails on a closed network, and silently redefines what "passing" means on the day its publisher ships a release. A regression guard whose criteria can move on their own is not a guard.

What has been found and fixed

Color contrast failures on forty-three elements, taken to zero. An invalid sort attribute on a table header. A link left indistinguishable from the text around it. Duplicate element identifiers on two pages carrying several forms at once — and, found by accident while chasing that, a form control with no accessible name at all, which the scanner never flagged and which is now recorded as a gap in what is systematically checked rather than quietly closed.

A real skip link was added, one that moves keyboard focus into the content rather than only scrolling the page to it. The focus indicator was rebuilt as concentric light and dark rings after the existing one was measured and found to fail contrast on two of the three backgrounds it was used against — including the tab strip, where the selected tab passed only because it happens to sit on navy, and every other tab, which is where arrow-key navigation constantly lands, was close to invisible.

Caption support was built for uploaded video, stored as WebVTT and served from your own deployment through the same permission-checked route as every other file. Reduced-motion preferences are honored across the interface, and that was measured rather than assumed from a library's documentation.

The part that matters most

This is not a VPAT, and we will not describe it as one

What exists is an internal engineering assessment. Its findings come almost entirely from automated testing, and automated testing of this kind reaches roughly a third of the WCAG 2.1 AA success criteria — the mechanically decidable third. The other two thirds have not been assessed.

Three things a conformance report would require have not happened: a genuine screen-reader pass by somebody who uses one competently, a genuine keyboard-only pass driven by a person rather than asserted by a test, and the judgment calls no tool makes — whether the reading order makes sense read aloud, whether a label says something useful rather than merely being present, whether focus lands somewhere sensible when a dialog opens and returns when it closes.

So the honest summary is this: the mechanically detectable failures we know how to find are fixed, and a large amount remains genuinely untested. If your process needs a VPAT or an Accessibility Conformance Report, we do not have one, and you should hear that from us before a decision rather than after it.

One known gap, stated with its number. Three hundred and seven elements across twenty-one pages have no measured contrast ratio at all. The page background is a gradient over two repeating textures, so the scanner cannot resolve a single color behind those elements and declines to judge rather than guessing. Some of them are certainly fine. Nothing currently knows which. Closing it needs manual sampling at the lightest and darkest points of that gradient, and that has not been done.
The authoring-time check is a different thing. The pre-publication check an author runs on a course looks for a fixed list of problems in authored content — missing alt text, a frame with no title, video with no captions recorded. It is an authoring aid, it says so itself, and it is not an audit of the platform.

Appearance

Three themes, and the choice belongs to the person rather than the installation.

Guidepost carries a Standard appearance — the one it has always had, unchanged, and still what every account gets until somebody chooses otherwise — alongside a Light theme and a Dark theme. A fourth setting simply follows whatever the reader's own device asks for.

The preference lives on the person rather than in site-wide settings, and the reason is an argument that only runs one way. Light-on-dark is harder to read for many people with astigmatism; a bright ground is harder for many people with photophobia. An installation-wide theme therefore takes an accommodation away from whichever group it did not pick. A per-person choice takes nothing from anybody.

The theme is resolved on the server and is in the markup before the page begins rendering, so nobody watches the wrong one flash past first — and no script is involved, which matters on a deployment whose content security policy is as narrow as this one's. The picker sits in the navigation bar rather than inside a settings page, because burying an accommodation costs the people who need it most.

Verified separately, three times over

A ratio measured on one ground proves nothing about another

Contrast is computed against rendered color, and every ratio the original assessment recorded had been measured against two grounds that both change in both new themes. Nothing carried across. Sixty-four page scans were run over the three themes at zero violations, and the coverage floor is asserted per theme — left as one global number, one theme's pass would have covered for another that had silently stopped rendering.

The Standard appearance still carries ten contrast shortfalls. They are colors the product already ships, kept deliberately rather than changed underneath existing customers. Each is recorded with its measured ratio, and a test fails if one of them gets worse — and also if one silently starts passing, which would leave the list claiming a failure that no longer exists. Light and Dark clear all ten.

Technology

A deliberately conventional stack.

Nothing here is novel, and that is the point — an institution taking custody of a deployment should recognize every part of it.

Django 6.1

On Python 3.14.

MySQL

In production; SQLite for local development.

No build step

Server-rendered templates with progressive enhancement.

5,800+ tests

Run on every change before it ships.

Server-rendered, no frontend framework

Pages are generated on the server with progressive JavaScript enhancement layered on top. There is no frontend framework and no build pipeline, which means no compiled asset bundle to reproduce and no toolchain to keep alive inside a closed network.

What integration exists today

A versioned, token-authenticated REST API, which works now and is carried by every deployment. Connections to a company directory, to external student records systems and to training-completion systems (SCORM) are written but unproven — they have never been pointed at a real system, so treat them as in development rather than as switches. Where each one stands →

On documentation. This page summarizes the security and deployment posture at a level appropriate for a public site. A full deployment and security handoff guide, along with detailed technical documentation, is available to an evaluating team on request.

Next step

Request the technical documentation.

Tell us about your environment and what your evaluation needs to cover. We will send the detail that matches it.