graphqlAD
A read-only GraphQL interface over the msad
Active Directory library, built with Strawberry + FastAPI, managed with
uv on Python 3.14.
Only LDAP search operations are exposed. The schema has a single Query type
and no Mutation type, so no write operation (create / modify / delete /
password reset / group membership changes) is reachable through the API.
It is a GraphQL sibling of restAD (REST) and
mcpAD (MCP): all three are thin, read-only
front ends that call the same typed msad API directly.
How configuration is resolved
The effective connection config (an msad.DomainConfig) is built in two layers,
with the environment always winning:
- msad TOML file (optional) —
~/.msad.tomlby default, or the path inMSAD_CONFIG_FILE. TheMSAD_DOMAINvariable selects which[domains.<name>]section to use (otherwise the[defaults] domain). MSAD_*environment overrides — applied on top of the TOML values.
This lets you keep non-secret settings in the TOML file (or omit it entirely) and inject credentials and any connection parameter at runtime via the environment, so secrets never need to live on disk.
Environment variables
| Variable | Overrides / effect |
|---|---|
MSAD_CONFIG_FILE |
Path to the msad TOML file (default ~/.msad.toml). |
MSAD_DOMAIN |
Which [domains.<name>] section to use. |
MSAD_HOST |
Directory host (e.g. dc.example.com). |
MSAD_BASE |
Search base DN (e.g. dc=example,dc=com). |
MSAD_PORT |
Port (int, e.g. 636). |
MSAD_USE_SSL |
LDAPS on/off (true/false/1/0/yes/no). |
MSAD_USER |
Bind user. Setting user and password enables simple bind. |
MSAD_PASSWORD |
Bind password. |
MSAD_QUERY_TIMEOUT |
Per-operation receive timeout in seconds (int). |
host and base are required (from either the TOML file or the environment).
Authentication:
- Omit
MSAD_USER/MSAD_PASSWORDand leave them unset in the TOML → Kerberos/SASL bind (runkinitfirst). - Set both
MSAD_USERandMSAD_PASSWORD→ simple user/password bind.
Server variables
| Variable | Effect |
|---|---|
GRAPHQLAD_HOST |
HTTP bind host for the graphqlad command (default 127.0.0.1). |
GRAPHQLAD_PORT |
HTTP bind port (default 8000). |
GRAPHQLAD_LAZY_CONNECT |
If true, start even if the directory is unreachable at boot. |
GRAPHQLAD_GRAPHIQL |
Serve the in-browser GraphiQL IDE at /graphql (default true). Set to false to disable it. |
Install
uv sync
The msad dependency is pulled from PyPI (msad>=0.8.2). Because msad
depends on gssapi, installing it builds a C extension against the system
Kerberos libraries, so you need the krb5 development headers available (e.g.
libkrb5-dev + krb5-config on Debian/Ubuntu, krb5 via Homebrew on macOS).
Common developer tasks run through the Makefile:
make sync # install/refresh the dev environment
make lint # ruff check + format check
make test # run the test suite
make build # build wheel + sdist into dist/
make publish # upload to PyPI (needs credentials)
Run
Credentials are passed via the environment:
export MSAD_HOST=dc.example.com
export MSAD_BASE='dc=example,dc=com'
export MSAD_PORT=636
export MSAD_USE_SSL=true
export MSAD_USER='svc_reader'
export MSAD_PASSWORD='s3cr3t'
uv run graphqlad
# or:
uv run uvicorn graphqlad.app:app --host 0.0.0.0 --port 8000
Then open the GraphiQL IDE at http://127.0.0.1:8000/graphql (the same URL
serves GraphQL POST queries).
Run with uvx
uvx runs graphqlAD in a throwaway, isolated environment — no clone or manual
install needed. The connection is still configured entirely through the
environment.
# From PyPI (once graphqlAD is published):
MSAD_HOST=dc.example.com MSAD_BASE='dc=example,dc=com' \
MSAD_USER=svc_reader MSAD_PASSWORD='s3cr3t' \
uvx graphqlad
# From a local checkout:
uvx --from . graphqlad
# Straight from the Git repository (no clone):
uvx --from git+https://github.com/matteoredaelli/graphqlAD graphqlad
All MSAD_* and GRAPHQLAD_* variables work exactly as above. Because uvx
builds msad (and its gssapi C extension) in a fresh environment, the krb5
development headers must be available on the host (see Install).
The schema
A single Query type exposes every read operation. Example query:
{
health { ok target auth error }
users(surname: "Rossi", department: "IT", limit: 10) {
count
entries # dynamic LDAP entries, as a JSON scalar
}
user(identifier: "mrossi") { entry }
userStatus(identifier: "mrossi") {
isDisabled
isLocked
hasExpiredPassword
hasNeverExpiresPassword
}
osSummary { count items { operatingSystem count } }
}
Available queries
health- Users —
users,inactiveUsers,user,userGroups,userStatus. - Computers —
computers,staleComputers,domainControllers,osSummary,computer. - Groups —
groups,emptyGroups,group,groupMembers,isMember. - OUs —
ous,ouContents,ou. - Audit —
domainInfo,passwordPolicy,passwordPolicyViolations,privilegedGroups,fsmoRoles,trusts,sites,gpos,gpo,fineGrainedPasswordPolicies.
Notes
- LDAP entries are dynamic (attributes depend on what the directory returns),
so each entry is exposed as an open
JSONscalar inside typed envelopes (count+entries, orentry). Non-JSON-native values (datetimes, bytes) are coerced to strings for transport. - A single LDAP connection is opened during the app lifespan and shared across requests; it is unbound at shutdown.
License
GPL-3.0-or-later. Copyright © Matteo Redaelli.
Metadata
Release files for graphqlad 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| graphqlad-0.1.0.tar.gz | 26.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| graphqlad-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.3 kB
Release files / graphqlad-0.1.0.tar.gz
| Download URL | graphqlad-0.1.0.tar.gz |
|---|---|
| Size | 26.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
222a8e482988b58356e1dccb2d61a00c78a9e10a0ef599c8e0e818bb7a493383
|
|
BLAKE2b-256 checksum How to use checksums |
e4d847a6904b62c6a51562ebd279ab77eae8876e7e9d5ce1cc87f7133d86099b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / graphqlad-0.1.0-py3-none-any.whl
| Download URL | graphqlad-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
68bed1f603c1dbf31900921311014c247fc13786b1ffe20b89155b9d02ec592e
|
|
BLAKE2b-256 checksum How to use checksums |
eb7b1f1ab245cf31e8b91eb84c33fce900d320efb68fc30cce8403c7900eea28
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for graphqlad 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.