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.
- Node.js
>=24 <25 - RHEL/systemd-friendly Linux
- No Docker, no database
Install from npm:
npm install -g @soft9tech/s9terpsyncOr build and run from source:
git clone https://github.com/soft9tech/s9terpsync.git
cd s9terpsync
npm ci
npm run builds9terpsync 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-runIf 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.
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
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.
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, andsilent, matched case-insensitively (DEBUGanddebugare 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'sRestart=on-failureit 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.
Full setup, configuration, and operations guidance lives in the project wiki:
- Home — landing page and index of all wiki content
- Installation & Requirements — supported platforms, install paths, and what gets installed
- Ethos & Banner Prerequisites — Banner integration user, Ethos application/proxy setup, resources required
- Configuration Reference — every field in
s9terpsync.yaml, explained - Running S9TerpSync — manual runs, resuming, and lock recovery
- Service & Systemd Operation — running as a long-lived systemd-managed service
- Retention, Replay & Recovery — retention policy, replaying incomplete runs, retry manifests
- Diagnostics & Troubleshooting — local diagnostics, contract status, and what to check when a run fails
- Architecture Overview — module boundaries and design constraints for contributors
Licensed under Apache-2.0.