Skip to content

v0.1.0

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Sep 13:04
· 118 commits to main since this release
0186861

The first release. Everything below existed before it; naming it is what CW-028
was for, so that an installation can say which version it is running instead of
"whatever main was that day".

Tags are cut by the Release workflow from
the top entry of this file, so adding a ## [x.y.z] section here and pushing
it is what makes a release. See
#26 for why it is a workflow
and not a command somebody runs.

Added

  • Employees — effective-dated records, employment events, org structure,
    offboarding with a scheduled final working day.
  • Leave — entitlements with accrual and carry-over, a balance ledger that
    reserves on submission, Thai public-holiday and weekend handling.
  • Attendance — clock in/out with a geofence flag, shift derivation, overtime
    requests, a nightly close-out that marks no-shows and incomplete days.
  • Payroll — effective-dated compensation, Thai withholding tax and social
    security, overtime multipliers, payslips, a run lifecycle with separation of
    duties between the person who runs it and the person who approves it.
  • Recruitment — job requisitions, a public careers page, applications with
    PDPA consent and a retention purge, assessments.
  • Performance — review cycles, KPI and competency scoring, acknowledgement.
  • Documents and files — document requests resolving to merge data, uploads
    checked by MIME type, extension and magic bytes.
  • Approvals — a policy engine with conditions, ordered steps, delegation and
    SLA due dates, shared by every module that needs a decision.
  • Audit — append-only at the database level, enforced by a trigger.
  • The HR assistant — answers policy questions from the organisation's own
    documents, with tool scoping that cannot be talked out of its permissions.
    Off by default.
  • Clients — a React 19 admin console and a Flutter employee app that
    tolerates being offline.
  • Second-factor authentication (CW-001) — TOTP implemented against RFC 6238,
    required rather than offered for any account that can read sensitive employee
    data, run payroll, approve payroll or manage roles.
  • Malware scanning (CW-002) — uploads streamed to clamd before they are
    stored, so malware is never written down for a later change to expose. A
    scanner that is unreachable holds the file rather than passing it. Off by
    default; docker compose --profile av turns it on.
  • Shared rate limiting (CW-003) — THROTTLE_STORAGE=postgres puts the
    request budget in the database that is already there, so a limit belongs to
    the deployment rather than to whichever replica answered.
  • The transactional outbox (CW-006) — domain events written in the same
    transaction as the change that caused them, relayed by a poller claiming
    batches with FOR UPDATE SKIP LOCKED. Retries with backoff, dead-letters what
    cannot be delivered, and keeps the dead letters.
  • Leader election for scheduled jobs (CW-007) — each nightly task takes a
    Postgres advisory lock, so running three replicas runs the work once.
  • Email and push delivery (CW-005) — SMTP written against RFC 5321 and FCM's
    HTTP v1 API, both registered as outbox handlers. Per-user preferences and a
    signed public unsubscribe link. Off by default; a channel switched on with
    incomplete configuration refuses to boot.
  • An end-to-end test suite (CW-011) — 103 checks driving the real
    application over HTTP, including two and three instances sharing one database.

Changed

  • Notifications commit with the change they announce. Previously a business
    transaction committed and the notification followed in a transaction of its
    own, so a crash in between left an approval nobody was told about. Four call
    sites gained a transaction they never had — approving a resignation wrote
    three rows that could always have disagreed with each other.
  • Multer errors are mapped by code rather than by message. Nest's own
    mapping matches message text; multer 2.4 reworded one, and every affected
    request became a 500 with a stack trace.
  • The upload endpoint states its whole contract as parser limits — one part,
    named file, no text fields — which is what neutralises the field-name
    denial-of-service advisories rather than the version bump alone.

Fixed

  • The documented Docker quick start could not migrate or seed (CW-000). The
    API image is pruned to production dependencies, so the commands the README
    gave had no Prisma CLI and no ts-node.
  • npm run test:e2e pointed at a config that did not exist (CW-011).
  • Nine high-severity advisories in shipped dependencies (CW-020) — multer
    and deepmerge-ts, pinned through npm overrides because neither parent had
    picked the fixes up.
  • The login screen's logo rendered as a full-width bar on every device: a
    56×56 square inside a CrossAxisAlignment.stretch column.

Security

  • Argon2id password hashing, AES-256-GCM field encryption, refresh-token
    rotation with family reuse detection, deny-by-default authorisation, row-level
    visibility scoping, and an append-only audit trail enforced by the database.
  • CI fails on a new high-severity advisory in a shipped dependency, weekly as
    well as on every push.

Known limitations

Stated in full in security.md and
spec.md. The ones that decide whether you can run
this yet:

  • There is no way to create an organisation without loading the demo data
    (CW-022).
  • The Thai payroll and social-security rules have not been reviewed by anyone
    qualified (#36).
  • organizationId is not a tenant boundary. One deployment serves one
    organisation.
  • The employee app cannot register for push yet (CW-037).
  • This code has never had a penetration test, and no independent human has read
    every line — see How this was built.