Skip to content

Repository files navigation

S9TerpSync

S9TerpSync is a standalone, file-state Slate CRM to Ellucian ERP sync service for schools that need a small RHEL/Linux-friendly Node.js runtime without Docker, a database, or an external ETL platform. Version 1 supports Slate to Banner through Ethos APIs; Slate to Colleague is planned future support.

This project isn't a replacement for commercial ETL platforms. It's a small contribution to higher ed institutions that don't already have an ETL tool, or can't justify the cost of one, to help them sync data from Slate to Ellucian Banner or Colleague through Ethos APIs.

We're building the same sync process for other ETL platforms — Boomi, MuleSoft, and Jitterbit — in the coming weeks, and will publish those here as open-source pipelines too.

If your institution already has access to Ellucian Ethos Data Connect, we'd strongly recommend using that instead of any of these.

Requirements

  • Node.js >=24 <25
  • RHEL/systemd-friendly Linux
  • No Docker, no database

Install

Install from npm:

npm install -g @soft9tech/s9terpsync

Or build and run from source:

git clone https://github.com/soft9tech/s9terpsync.git
cd s9terpsync
npm ci
npm run build

Quick start

s9terpsync config init --source both --institution-id <school-id> --environment production --out /etc/s9terpsync/s9terpsync.yaml
s9terpsync config validate --file /etc/s9terpsync/s9terpsync.yaml
s9terpsync run --file /etc/s9terpsync/s9terpsync.yaml --source sftp --dry-run

If you built from source, run the same commands via node dist/cli.js in place of s9terpsync.

This quick start does not authorize scheduled LIVE production execution — dry-run review and explicit live-target configuration are prerequisites to that separate decision.

How data moves from Slate to Banner

A Banner person record is always matched or created first; every other entity depends on it (and admission decision, student attribute, and student cohort also depend on the admission application existing first). This is the real dependency order the stage engine enforces — src/stages/stage-registry.ts is the single source of truth for it.

flowchart TB
  slate["Slate CRM export<br/>SFTP file or API"]
  canonical["Decode, crosswalk,<br/>match to a Banner person"]

  subgraph entities["Nine target entities, in dependency order"]
    person["Person"]
    permanentAddress["Permanent address"]
    priorSchool["Prior school"]
    familyAddress["Family address"]
    familyRelationship["Family relationship"]
    admissionApplication["Admission application"]
    admissionDecision["Admission decision"]
    studentAttribute["Student attribute"]
    studentCohort["Student cohort"]

    person --> permanentAddress
    person --> priorSchool
    person --> familyAddress
    person --> familyRelationship
    person --> admissionApplication
    person --> admissionDecision
    admissionApplication --> admissionDecision
    person --> studentAttribute
    admissionApplication --> studentAttribute
    person --> studentCohort
    admissionApplication --> studentCohort
  end

  ethos["Ellucian Ethos APIs"]
  banner["Banner ERP"]

  slate --> canonical --> person
  entities --> ethos --> banner

  classDef pipeline fill:#EEEDFE,stroke:#534AB7,color:#26215C
  classDef mutation fill:#FAECE7,stroke:#993C1D,color:#4A1B0C
  classDef failure fill:#FAEEDA,stroke:#854F0B,color:#412402
  classDef readonly fill:#E1F5EE,stroke:#0F6E56,color:#04342C
  classDef neutral fill:#F1EFE8,stroke:#5F5E5A,color:#2C2C2A
  class canonical pipeline
  class person,permanentAddress,priorSchool,familyAddress,familyRelationship,admissionApplication,admissionDecision,studentAttribute,studentCohort,ethos,banner mutation
  class slate neutral
Loading

Core entities:

Entity What it carries Ethos resource
Person Match an existing Banner person (by Banner ID or Slate ID) or create one persons v12
Permanent address The applicant's Banner address persons v12
Prior school High school code, graduation date, GPA high-school-information-high-school-details v1
Family address Parent/guardian address general-person-identification-address v1
Family relationship Parent/guardian record and relationship type guardian-information v1
Admission application Application header, curricula, term/level/campus/program admissions-application-curricula v1
Admission decision Admit / deny / waitlist code and date admissions-decision-processing v1
Student attribute Attribute records tied to the application admissions-application-contacts-cohorts-attributes v1
Student cohort Cohort records tied to the application admissions-application-contacts-cohorts-attributes v1

Full detail on each of these — including exactly what to grant on the Ethos/Banner side — is in the Ethos & Banner Prerequisites wiki page.

Logging

Both the CLI and the service emit structured logs as JSON — one object per line — on stdout. Nothing is written to a log file. Under the packaged systemd unit that stream lands in journald, alongside the plain-text human-readable output the process also writes (startup diagnostics, uncaught stack traces), so read it back with a filter that skips the non-JSON lines rather than a bare jq: journalctl -u s9terpsync -o cat | jq -R 'fromjson? // empty'.

Verbosity is controlled by the LOG_LEVEL environment variable. There's no s9terpsync.yaml field and no CLI flag for it — the environment variable is the only control.

  • Valid values are fatal, error, warn, info, debug, trace, and silent, matched case-insensitively (DEBUG and debug are the same value).
  • Unset or empty means info.
  • Anything else is rejected rather than ignored. The CLI writes a fixed diagnostic naming the seven valid levels — it never echoes the value you supplied — and exits 1. The service fails to start: it throws during startup, before it binds its HTTP port, so under the unit's Restart=on-failure it restart-loops until the value is corrected.

The level is read once, at startup. Changing it means restarting the process; a running service won't pick up a new value on its own. Setting LOG_LEVEL on the systemd unit is covered in Service & Systemd Operation.

Documentation

Full setup, configuration, and operations guidance lives in the project wiki:

License

Licensed under Apache-2.0.

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages