Employee portal compatibility and private migration
Recorded 2026-11-04. The clinical director accepted the collected, non-PHI staff and management portal content and the interim shared-code presentation. Browser-side decryption remains access friction rather than individual authentication, revocation, a secure session, or a HIPAA determination.
The same successfully validated code authorizes protected document downloads
for that role during the current page session. The browser keeps it only in a
closure, performs a same-origin authenticated fetch, and saves the returned
document without opening nginx's native Basic Auth dialog. Staff uses the
staff username and stream b; management uses the management username and
stream a. Direct URLs remain nginx-protected, cross-role fetches are rejected
before credentials are sent, and failed authenticated downloads show an inline
error without direct-navigation fallback. No code enters a URL, browser
storage, public artifact, or log. Downloads use a bounded safe form of the
approved link label and preserve the validated extension from the protected
SHA-256 server destination; server resources are not renamed.
Approved production ciphertext
An indexing-enabled production build may include one owner-approved encrypted two-stream payload when both of these non-public build inputs are set:
AIA_PORTAL_PRODUCTION_APPROVED=true
AIA_PORTAL_PRODUCTION_CIPHERTEXT_FILE=/absolute/private/employee-portal/approved-production-ciphertext.json
The build rejects missing, malformed, symlinked, oversized, wrong-mode, or
simultaneous local-preview/production inputs. It never decrypts the payload at
build time. /employee-portal remains noindex, sitemap-excluded and
hreflang-excluded. See the deployment runbook for the
approved release and protected-resource installation commands.
Source and private collection
The 42 approved non-PHI resources have already been collected under the ignored
private/employee-portal/hubfs/ workspace. Collection and clinical review are
complete. Do not run a new collection pass for production preparation.
Production preparation is an owner-only, hidden-prompt sequence. The first command identifies the encrypted streams from the staff and management codes; the codes are never accepted in arguments, environment variables, files or output:
npm run portal:prepare -- identify \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
npm run portal:prepare -- propose-mappings \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
Review the private mapping summary. The role rule maps staff-only references to
/staff-resources/, management-only references to /management-resources/, and
duplicates a shared source into both protected namespaces. After the owner
approves that operational policy, run:
npm run portal:prepare -- approve-mappings \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
npm run portal:prepare -- rewrite-production \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
The rewrite command prompts for both codes again, rewrites every approved legacy
resource URL, re-encrypts with fresh salts and IVs, verifies each role privately,
and atomically installs approved-production-ciphertext.json plus a matching
private production-readiness.json. It refuses unresolved /hubfs/ references.
Prior manifests and production ciphertext are retained as timestamped private
backups.
Mapping approval versus server verification
mappingApproved, accessPolicyApproved, and rewriteApproved mean the
verified collected source has an approved role and safe protected destination
and may be prepared or installed. They do not claim the destination exists.
destinationInstalled, destinationHashVerified,
destinationPermissionsVerified, and destinationAccessVerified remain false
until an authorized VPS rehearsal actually installs the file and verifies its
bytes, root:nginx permissions, Basic Auth boundary and protected URL. Local
preparation and installation accept the first state; release acceptance must
still complete the second state before browser cutover.
Read-only legacy footer source:
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt.
The original stays outside Git as rollback evidence. The 42-file collection and
its accepted content are complete; no current command downloads resources.
Private output is private/employee-portal/: directories 0700, manifests and
binary resources 0600. This exact subtree is gitignored and outside Astro's
public directory. Filenames are SHA-256 hashes of source URLs, independently
verified by content hashes. The manifest retains original references, labels,
view membership, MIME, bytes, status and delivery/classification decisions.
The manifest preserves the original source URL, collection hash, size, reference
membership and accepted source evidence. Public legacy reachability did not
infer public approval.
Before every artifact transfer, verify that only dist is copied and that it
contains no private manifest, document or real ciphertext unless that ciphertext
has separately been authorized for the local preview. Never archive the whole
working directory as a deployable artifact.
Historical collection and rewriting recovery notes
The original source, collection manifests, collected bytes and prior ciphertext revisions remain recovery evidence. Do not change their classifications or destination flags by hand; use the production-preparation sequence above so stream identity, references, source hashes and protected destinations remain consistent. CSS/srcset dependencies remain an explicit error rather than a silent rewrite.
Local preview and content safety
Use a retained private ciphertext revision:
AIA_PORTAL_LOCAL_PREVIEW_APPROVED=true \
AIA_PORTAL_CIPHERTEXT_FILE=/absolute/private/ciphertext-TIMESTAMP.json \
PUBLIC_AIA_FORMS_MODE=off PUBLIC_ALLOW_INDEXING=false \
npm run dev -- --host 127.0.0.1
The 2026-10-30 revision removes dependence on the retired Form A environment role. Form mode is independent; off above is optional for local testing and cannot grant portal approval. The page defaults to locked/unavailable without its own explicit approval and valid private input. Indexing
builds cannot include it. The codes never go to a server, storage or URL. No
native form submits; Enter and Unlock use local WebCrypto. Only the matching
stream is displayed; refresh or leaving the page locks it again.
The passive DOM renderer preserves headings, lists, text, tables and safe HTTPS/mailto/tel links, with no referrer on links. It rejects scripts, inline handlers, styles, media, embedded forms and unknown tags. Real portal markup has not been decrypted or reviewed; any removed behavior must be reviewed with the owner before claiming fidelity. It must not silently become arbitrary script execution. No real encrypted stream or plaintext is in Git.
Scoped presentation correction, 2026-10-30
The renderer now maps only hs-button, content-button, or
portal-resource-button anchor tokens, or exact inline AIA orange background
colors (#ffbd59 / #ff914d), to the local portal-resource-button class.
It does not copy the original style or arbitrary classes. These are narrow
compatibility candidates, not a claim that encrypted real views have been
inspected. HTTPS/mailto/tel links retain the existing safe URL checks; contact
links receive a local dark-orange accent for readable contrast. Table scope
and bounded numeric row/column spans are preserved. All other executable or
unapproved attributes remain removed.
The follow-up styling correction recognizes the live portal's Daily Lineup Report and Monthly Lineup Report link labels, even when their original
HubSpot classes or inline fills are absent. It maps only those links to the
existing orange resource-button style; arbitrary anchors remain ordinary links.
Both unlocked views now use the site's orange accent for list-item markers.
The source classes and styles still do not reach the passive DOM.
Staff resources and Management Templates, 2026-11-01
The owner observed that the non-management (general-staff) portal still lost
HubSpot's resource-button presentation. After sanitizing the decrypted DOM,
a headed Management Templates section identifies the general-staff resource
view. Valid HTTP(S) resource links in that view receive only the local
portal-resource-button class; mailto: and tel: retain contact-link
styling. Neither destination validation nor removed legacy classes/styles,
scripts, media or HTML attributes are relaxed.
The Management Templates unordered list receives a local
portal-template-grid class: two columns at desktop widths (768px+),
one column on smaller screens. Semantic list order and keyboard tab order
are retained, with responsive full-width buttons. This is a presentation
change only: it does not infer or approve employee resource access.
Synthetic staff/management browser fixtures check the distinction and
responsive columns. The actual decrypted staff source remains private;
owner should confirm layout and links in the local preview before merging.
Scoped typography, spacing, lists, table rules and keyboard focus apply to both unlocked streams. Synthetic staff/management fixtures exercise these mappings at 320/390/768/1440px. Owner must inspect the real views privately to confirm legacy button recognition and source fidelity; if a different class is used, provide sanitized class semantics rather than decrypted content or credentials.
Before owner acceptance: validate two real view codes privately, inspect every resource/category, responsive directories, focus, link semantics and sanitization impact. Check wrong-code feedback, keyboard unlock, no-JS explanation and refresh. Production must use a separately reviewed runtime ciphertext delivery mechanism; this batch intentionally provides only a gated local build input. Browser-delivered ciphertext is publicly downloadable by anyone able to reach that preview.