Start Here
IdentityScribe syncs your LDAP directories into PostgreSQL and exposes the data through REST, GraphQL, and LDAP channels. This page walks through installation, configuration, and your first queries. Takes about 5 minutes.
From download to first query in minutes
Prerequisites
Section titled “Prerequisites”- PostgreSQL database (17+, recommended 18+)
- Access to an LDAP directory (Active Directory, OpenLDAP, etc.)
- curl or any HTTP client
- ldapsearch (optional, for testing the LDAP channel)
Step 1: Download
Section titled “Step 1: Download”Step 2: Configure
Section titled “Step 2: Configure”We need a config file that points at our database and LDAP source. Create identity-scribe.conf:
# Where to store synced datadatabase.url = "jdbc:postgresql://localhost:5432/scribe"database.user = "scribe"database.password = ${DB_PASSWORD}
# LDAP source to sync fromldap.url = "ldap://your-ldap-server:389"ldap.bind-dn = "cn=reader,dc=example,dc=com"ldap.bind-password = ${LDAP_BIND_PASSWORD}
# What to sync -- one transcribe per entry type# The key ("users" here) becomes the entry type we query againsttranscribes { users { ldap { base = "ou=users,dc=example,dc=com" filter = "(objectClass=person)" scope = sub attributes = "cn, sn, givenName, mail, telephoneNumber" } }}
# Serve the HTTP API channels (both are disabled by default)channels.rest.enabled = truechannels.graphql.enabled = true
# Serve the LDAP channel on 10389 (enabled by default; needs a listener to start)channels.ldap.listen = [ { port = 10389 } ]Secrets use ${ENV_VAR} substitution so they stay out of config files. Each transcribe defines an entry type to sync, and its ldap block sets the source base DN, filter, scope, and which attributes to capture. For relationship fields such as groups, roles, managers, or owners, see Attribute Modeling before deciding which side to store. The REST and GraphQL channels are off by default, so we enable both here to query over HTTP in the next steps. The LDAP channel is on by default but serves only once it has a listener, which we add on port 10389 for the LDAP query later. See API Access & Networking for what each channel exposes.
Step 3: Run
Section titled “Step 3: Run”export DB_PASSWORD="your-db-password"export LDAP_BIND_PASSWORD="your-ldap-password"
./identity-scribe-* --mode devWe should see a startup banner with the version and configured channels. Once it's ready, we verify:
curl http://localhost:8080/readyzExpected output: ok. If we see this, Scribe can safely receive traffic. Full initial sync and index work may still be continuing; use /observe/convergence when you need to wait for everything to finish. If the binary fails with Illegal instruction or similar on startup, see CPU Compatibility.
Step 4: Query via REST
Section titled “Step 4: Query via REST”# List all userscurl http://localhost:8080/api/entries/users | jq
# Search by name (FleX filter syntax -- human-readable shorthand)curl -G http://localhost:8080/api/entries/users --data-urlencode "filter=cn ^= John" | jq
# Get a specific entry (use an entryID from the list response above)curl http://localhost:8080/api/entries/users/ENTRY-ID-HERE | jqWe should see a JSON response with an edges array. Each entry includes its entryID, cn, mail, and the other attributes we configured in the transcribe.
Scribe auto-detects the filter format, so we can also use LDAP syntax (cn=John*), JSON {"cn":{"sw":"John"}}, or SCIM cn sw "John". FleX is the human-readable shorthand format. See Choosing Filters for trade-offs between formats.
Step 5: Query via GraphQL
Section titled “Step 5: Query via GraphQL”curl -X POST http://localhost:8080/graphql \ -H "Content-Type: application/json" \ -d '{"query": "{ users(filter: \"cn ^= John\") { edges { node { cn mail } } } }"}' | jqThe response wraps results in data.users.edges. Each node contains the fields we asked for — cn and mail in this case.
The GraphQL schema is generated from our transcribe configuration. Open http://localhost:8080/graphql in a browser to explore it interactively with the built-in GraphiQL IDE.
Step 6: Query via LDAP
Section titled “Step 6: Query via LDAP”# List all entriesldapsearch -H ldap://localhost:10389 -x -b "o=data" "(objectClass=*)" cn
# Search users by nameldapsearch -H ldap://localhost:10389 -x -b "ou=users,o=data" "(cn=John*)" cn mailThe LDAP channel prints entries in LDIF format — one attribute per line, entries separated by blank lines. We should see the same data we queried via REST and GraphQL.
Step 7: Explore the UI
Section titled “Step 7: Explore the UI”Open http://localhost:8080/ui in a browser. The Operator UI shows synced entry counts, live ingest activity, and pressure indicators. We can search and filter entries, view point-in-time snapshots, compare versions, and browse the change timeline for any entry.
The Observe dashboard at /ui/observe shows golden signals, service health, and the Operator Copilot — which surfaces recommendations when it detects problems.
Observe requires authentication by default. For a trusted local walkthrough, use the recommended template's Observe opt-out; for shared environments, sign in before opening Observe.
What's next
Section titled “What's next”| Goal | Page |
|---|---|
| Understand the data flow | Architecture |
| Learn how sync works | Change Detection |
| Tune your config | Configuration |
| Add auth and TLS | Production Checklist |
| Set up monitoring | Monitoring |
| Fix issues | Troubleshooting |
| Browse all config keys | Reference |