Internalize before writing code. All first-hand observed (2026-08-10), not assumed.
-
One host, one front controller. Every program is dispatched through
https://www.previred.com/wPortal/CtrlFce?reqName=<programa>. There is no per-area host and no public API. The portal is the source of truth; any program name found in devtools is internal and may change without notice. -
The URL tells you nothing about your session. An unauthenticated request returns HTTP 200, unredirected, at the same URL, rendering "SU SESIÓN HA EXPIRADO". Authentication is therefore a content judgement, and it is made fail-closed: a positive marker means authenticated, anything unrecognised means not authenticated (ADR-003). This is the single biggest structural difference from the
siiproject. -
This portal moves money. Previred exists to declare and pay cotizaciones previsionales. A read and a payment differ by one string in one query parameter, so the core enforces an allowlist of dispatchable programs at runtime, pinned by test (ADR-004).
-
The login form must be driven, not synthesised.
#tokenis regenerated per page load and the page's own JS derivesweb_rut/web_dv. A hand-built POST is both fragile and pointless (ADR-005). -
Production-only. Previred URLs are constants in
core/config; never hard-code one elsewhere. There is no env switch.
previred/
├── packages/
│ ├── core/ (@dostertags/previred-core) # All domain logic. A Node library.
│ └── cli/ (@dostertags/previred-cli) # The `previred` command.
└── docs/ # ADRs + observed wire contracts
An MCP server is planned but deliberately absent: with only auth tools it would give an assistant nothing to do, and a headed browser launched from a background stdio server is awkward. It arrives with the first real read surface.
previred-core is a normal Node library that MAY use Node APIs. What it keeps
is a small set of injectable seams so unit tests never touch the real portal,
the real filesystem, or the wall clock (ADR-002):
| Seam | Purpose | Node default |
|---|---|---|
PortalDriver / PortalSession |
Drive the JS-heavy portal | Playwright/Chromium |
PortalDriver.requestPublic |
Session-less public fetch (ADR-010) | Node fetch — no browser |
KeyValueStore |
Persist the cookies-only session | ~/.previred/*.json (ACL-hardened, ADR-007) |
AuditSink |
Append-only JSONL receipt | ~/.previred/audit.jsonl |
Clock |
Time + the login poll interval | System clock |
PortalSession.snapshot() is the seam that makes ADR-003 work: it returns
{ url, title, text } as plain data, so the classifier is a pure function and
the entire auth decision is unit-testable with no browser.
human (CLI) ──▶ tasks/ ──▶ auth/ ──▶ guard/readOnly(session) ──▶ PortalDriver
│ │
│ └── reqName allowlist (ADR-004)
└── audit receipt, session lifecycle
- Surfaces call tasks only. Reaching past the task layer bypasses the audit receipt and the session lifecycle.
readOnly()is applied centrally — byloginon a fresh session and bywithSessionon a restored one — so no facade can obtain an unguarded session, not even by accident. Because it lives in the core rather than the adapter, the fake session in tests is guarded identically.- Only
loginmints a session. Domain tasks consume one viawithSession.
| Module | Purpose | Status |
|---|---|---|
rut |
RUT parse / canonicalise / Mod-11 (in-house) | Done |
config |
Prod URLs + settings | Done |
config/reqnames |
The read-only allowlist + write-token denylist (ADR-004) | Done |
seams |
PortalDriver / PortalSession / KeyValueStore / AuditSink / Clock |
Done |
guard |
readOnly() session decorator + assertNavigable |
Done |
auth/markers |
Pure fail-closed page classifier (ADR-003) | Done — authenticated marker unpinned |
auth |
Headed login (polling), logout, local + live status | Done |
capture |
Redacted structural capture (ADR-006) | Done |
audit |
Append-only JSONL receipt, secret keys dropped | Done |
tasks/auth |
What the CLI calls | Done |
html/tables |
In-house HTML table reader (pure, tested) | Done |
portal/indicadores |
Public, session-less indicadores previsionales (ADR-010) | Done |
portal/pagos |
Historial de pagos / planillas pagadas | Planned |
portal/nomina |
Trabajadores — blocked on ADR-008 | Blocked |
portal/certificados |
PDF downloads (needs a requestBinary seam) |
Planned |