Source-available platform for IT asset lifecycle automation. Built for on-premises datacenters, replacing expensive commercial tools.
Give your users a self-service portal to request, extend, and return IT assets (VDIs, application access, infrastructure resources) while your IT team keeps full control through configurable approval workflows, runbooks, and audit trails.
ip·Solis is source-available under the XenPool Commercial Source License v1.0.
- Free tier — productive use for up to 25 active users, including in a commercial, governmental or other organizational context. No license key required. (An active user is a distinct person — by email — holding at least one active asset request/assignment; ended ones don't count.)
- Free use — also purely private use by individuals, and non-productive research/teaching at state-recognized educational institutions.
- Evaluation — 30 days free in any environment, no license key required.
- Commercial use — productive use with more than 25 active users requires a valid license purchased from XenPool GmbH, offered in volume bands by active-user count (e.g. 26–75, 76–250, 250+) — regardless of profit intent, and including public-sector bodies and non-profit / charitable organizations. Get a license at the ip·Solis license shop.
All features ship in a single image — the tiers differ only by active-user count, never by features.
Enterprise IT automation shouldn't require a 6-month implementation project and a six-figure license. This platform was born from 30 years of datacenter operations experience and is designed to be deployed in an afternoon.
- Users request assets through a clean web interface
- Single sign-on via OpenID Connect — Entra ID, Okta, Ping, Google, Keycloak, … (any compliant IdP)
- Order tracking with real-time status updates
- "My IT" dashboard showing active assets with extend/modify/cancel options
- Deputy support (order on behalf of another user)
- Multi-language UI (English, German, Spanish, French, Italian)
- Catalog search and category filter in the request page (auto-shown for catalogs with more than ~6 definitions)
- Long-form help text per asset definition (admin-authored markdown, sanitized) shown when the requester picks a type
- Configurable per asset type: manager approval, application owner approval, or both
- Manager looked up automatically from Active Directory
- Re-approval on asset modification (optional per asset type)
- Email notifications to approvers with one-click approve/decline
- Microsoft Teams approval cards — Adaptive Card delivered via Workflows webhook with a tokenized link that lets approvers decide without logging into the portal
- Approval reminders — a Beat task re-sends email + Teams notification after a configurable interval (default 24 h, capped at 3 nudges) when an approver hasn't decided
- Approval delegation (OOO mode) — admins configure deputy windows ("Stefan is on vacation Aug 1–15, route his approvals to Jupp"); new orders during the window automatically address the deputy, original assignee captured in the audit trail
- Self-service delegation in the portal — managers configure their own OOO without going through an admin (
/portal/delegations); identity is enforced server-side so a user can never re-route someone else's approvals - Approval escalation — once an approval has burned through all its reminders without a decision, the configured escalation contact(s) get notified. Two modes: notify-only (default) emails the contact pointing at the admin UI for operational intervention; assignment mode (opt-in via
approval.escalation_assign=true) creates a newOrderApprovalrow per contact with a tokenized one-click/approve/<token>URL so the contact can decide directly from their inbox. Each original approval escalates at most once - N-of-M approvals — set
min_approvals_requiredper asset definition so any N of M configured approvers can satisfy the order. Per-rule N-of-M lets each conditional rule carry its own quorum; rule-driven approvers form an isolated group with that quorum, while manager / owner / no-quorum-rule approvers fold into the asset-type-level group. Per-bucket supersession auto-closes surplus pending rows in a bucket the moment its quorum is met (decision-time, not Beat-tick-time) so approvers in 2-of-5 groups don't keep getting reminders after the first two say yes; the whole-order supersession path still fires when every bucket meets. Decline still vetoes regardless of N - Conditional approval rules — JSONB rule list per asset definition adds extra approvers when a condition matches the order. Conditions can be a leaf
{field, op, value}or a compound{op: "and"|"or"|"not", clauses: [...]}— nested up to 8 levels deep. Built-in fields (duration_days,monthly_cost,has_pii/phi/pci,requester_department) plus anyattr.<key>from the asset type's user-supplied attributes. Visual recursive editor in the asset-type form: each compound renders an AND/OR/NOT op selector + child clauses; each leaf gets a "wrap in group" affordance; depth-coloured left borders make the tree structure obvious at a glance. Malformed rules are logged and skipped, never blocking order creation. - Per-classification approval routing — one-click toggle per data class (PII / PHI / PCI). Two routing modes: Compliance officer (centralised) auto-adds one approval step at the global compliance-officer email (standard for centralised compliance teams); Owner of record auto-adds one step per entry in the asset type's
approval_ownerslist (standard for HIPAA-style workflows where the data steward who actually owns the regulated surface must sign off). Strictest-first precedence (PCI > PHI > PII), de-duped against manager / app-owner / rule approvers - Auto-decline on extended inactivity — opt-in policy that system-declines pending approvals past a configurable age (e.g. 14 days) so stale requests don't pile up forever. Daily Beat task picks at most one stale approval per order, propagates the existing veto-on-decline semantics (order → rejected, requester emailed, audit row attributed to
system:auto_decline). Off by default; configure under Settings → E-Mail → Approval Reminders → Auto-decline (opt-in).
- Visual runbook builder in the Admin UI
- Three automation strategies: Group Access (AD/Entra groups), Runbook (PowerShell scripts), or Composite (both)
- PowerShell module management (install from Gallery or upload custom
.zip) with a per-module Linux compatibility flag (Linux ✓/Windows only ✕/Unverified ?) — the worker runs PowerShell 7 on Linux, and modules taggedPSEdition_Desktoponly won't load. Operators declare compatibility when adding a module and can flip the badge inline by clicking it in the modules table - In-app PowerShell script editor with parameter introspection
- Step-by-step execution tracking with structured JSON logs
- Ad-hoc or cron-scheduled runbooks that are not tied to an asset type
- Per-run tracking (history, logs, notes)
- Useful for housekeeping jobs, one-off operations, and scheduled reports
- Three assignment models: capacity-pooled (quotas), dedicated-shared (jump hosts), assigned-personal (1:1)
- Automatic expiry checks and reminder emails (Celery Beat)
- Configurable deprovision policies: access removal, return-to-pool, return-to-pool-with-reinstall, deallocation, deletion, or custom runbook
- Extended asset statuses —
Free,In use,Reinstall,Reinstalling,Failed,Maintenance - Scheduled orders (future-dated provisioning with asset reservation)
- Scheduled PostgreSQL backups (cron-driven) with retention policy
- Manual backup/restore/download via Admin UI
- Health probes (DB, Redis, Beat leader liveness, external system reachability, SIEM streaming) with email alerts on state transitions;
/healthis the unauthenticated load-balancer endpoint and/admin/maintenance/healthis the auditor-readable detailed view - Celery queue inspection and targeted purge
- Audit Log viewer at
/ui/audit-log— filterable by entity type, entity id, triggered-by substring, and time range; coloured actor badges show at a glance whether each row was driven by a bearer token, an admin session, the legacyX-Admin-Key, or a webhook; expand any row to see the JSON before/after diff
- Restrict asset types to specific AD groups (eligible requestors)
- Per-asset-type configuration for RDP and admin user management
- Capacity enforcement with pool availability checks
- Per-user quota (
max_per_user) for personal and pooled assignment models so one user can't exhaust the pool - Active / inactive flag on asset definitions — deprecate without losing history; inactive types disappear from the portal catalog but stay visible in the admin list
- Prometheus
/metricsendpoint — request count + latency histogram per route, plus business gauges (orders by status, pending approvals, pool free/busy by asset type, Celery queue depth per worker queue) - Cardinality-bounded route labels (path templates, not actual paths)
- Toggleable via the
metrics.enabledconfig flag - OpenTelemetry tracing — auto-instrumented FastAPI requests, SQLAlchemy queries, and Celery tasks flow through an OTLP HTTP exporter to any standard collector (Jaeger, Tempo, SigNoz, Honeycomb); a request that dispatches a runbook produces a single trace spanning api + worker; a console exporter mode is available for local verification without a collector
- Unified leaver flow — when a user leaves, ip·Solis revokes every active order owned by them, supersedes their pending approvals (so quorum logic doesn't stall), and supersedes any pending certification reviews assigned to them. Same code path regardless of how the signal arrives. Audit trail in
hr_leaver_eventswith per-action counts; admin viewer at/ui/leaver-events - HR webhook at
POST /hr/leaverwith HMAC fallback or scoped bearer token (hr:leaver). Adapters for ipSolis-native, Workday (workerId+eventType: terminated), SAP SuccessFactors (PERSON.PERNR), and Microsoft Graph subscription notifications (resourceData.userPrincipalName) - SCIM 2.0 endpoint at
/scim/v2/*— leaver-focused subset of RFC 7644 (ServiceProviderConfig, ResourceTypes, Schemas, Users CRUD). DELETE and PATCHactive=falsetrigger the leaver flow. Bearer-only auth viascim:read/scim:writescopes — drop-in target for Okta / SailPoint / Ping deprovisioning workflows
- Access certification campaigns — required for ISO 27001 / SOX / PCI compliance audits. Create a campaign with a scope filter (asset types / cost centers / departments / requester emails), kick it off to materialise one review row per (matching order, reviewer = the order's manager). Reviewers get a kickoff email + Teams card with a signed-token URL that opens a no-login review queue. Per row they pick Confirm (user keeps access) or Revoke (ip·Solis pulls access immediately via the deprovision runbook). Daily Beat task fires reminders at configurable offsets (default T-7d / T-1d), an overdue email past due, an escalation summary to a contact list, and auto-revoke on overdue (opt-in) so unreviewed access is pulled automatically. Manager portal page at
/portal/certificationsfor SSO users; admin drill-down at/ui/certificationswith per-status counts and inline confirm/revoke for stand-in scenarios
- SIEM audit-log streaming (Splunk HEC + Microsoft Sentinel + generic HMAC-signed webhook) — every
audit_logrow is forwarded once a minute to one of four back-ends: Splunk HTTP Event Collector, Sentinel via the legacy Azure Monitor Data Collector API (sunset by Microsoft on 2026-08-31 — kept for back-compat), Sentinel via the newer Logs Ingestion API (DCE / DCR + AAD service principal — recommended for new deployments), or a generic JSON webhook with HMAC-SHA256 body signing in a GitHub-compatibleX-Hub-Signature-256: sha256=<hex>header (configurable header name + extra headers for receivers like Datadog, Sumo, Loki, Elastic). Persistent cursor, automatic retry on transient failure, and a "Send Test Event" button verifies connectivity before enabling - Tamper-evident audit log — BEFORE-statement triggers on the
audit_logtable block DELETE / UPDATE / TRUNCATE by default; a documentedSET LOCAL ipsolis.allow_audit_mutation = 'true'escape hatch exists for legitimate retention maintenance - Audit retention pruning — daily Beat task auto-deletes audit rows older than the configured window using the bypass GUC. Per-classification windows (
retention.pii_days,retention.phi_days,retention.pci_days) sit on top of the globalretention.audit_log_daysso PII/PHI/PCI rows can be kept 7+ years while routine config changes drop after 90 days. Each row's classification is set at write time from the strictest class declared on the touched asset type's attributes;last_run_at,last_pruned, and a per-class breakdown (last_pruned_by_class) are tracked inapp_configfor ops visibility - Per-integration API tokens — replaces the single shared
X-Admin-Keywith named, expiring, revocable bearer tokens stored as SHA-256 hashes (raw token shown once on creation); legacyX-Admin-Keystill accepted as a fallback so existing integrations don't break on upgrade. Hard-delete retention policy (opt-in,api_tokens.purge_after_days): a daily Beat task hard-deletes tokens whoserevoked_atorexpires_atis older than the configured window, with a per-rowapi_token / hard_deletedaudit row capturing name + prefix + reason. Default0keeps slice-1 retain-forever behaviour for installs that prefer the audit-trail intact - Admin RBAC — five-tier role ladder (
superadmin > admin > approver > auditor > helpdesk) backed by per-user accounts inadmin_users(PBKDF2-SHA256 / 600k iterations, stdlib-only, no bcrypt/passlib build dependency). First-run setup auto-prompts for the first superadmin when the table is empty. Self-protection guards prevent superadmin self-demotion / self-deactivation / self-delete and block losing the last active superadmin. LegacyADMIN_API_KEYcontinues to authenticate as a virtual superadmin so existing scripts don't break. Audit attribution carries the role (admin:session:alice:superadmin) so auditors can filter by both who and with what authority. Comprehensive role gating: operational routers (modules, runbooks, maintenance, approval-delegations, asset-type CRUD, config writes) requireadmin+; infrastructure routers (license, seed export, initial setup, API tokens, admin user mgmt) requiresuperadmin; cost-report isauditor+ - Per-asset-type ACL grants — scope individual
adminusers to a subset of asset types. Granting an admin even one type flips them into "see only granted types" mode (the admin UI list filters automatically and out-of-scopePUT/DELETE/clonereturns 404 — same shape as a missing id, so the existence of unrelated teams' types isn't leaked). Zero grants = back-compat "see all" so single-team installs aren't surprised by the new feature.superadmin/approver/auditor/helpdeskalways bypass scoping. Auto-grant on create: when a scoped admin creates a new asset type, the grant is added automatically so they don't lose visibility on their own creation - Separation of duties (SoD) — a user who configured an asset type cannot also approve their own access requests against it. Detection walks the audit log for matching
created/updated/clonedrows attributed to the approver (matched on email, local-part, or admin username). Fires on approve only — declines stay open since rejecting your own work is always allowed. The blocked approver gets HTTP 409 with the original config-time audit attribution quoted back, and the approval row stayspendingso a different approver can decide - Bearer-token role binding — API tokens may be issued with a specific role (
superadmin/admin/approver/auditor/helpdesk) on top of their scopes. Role-gated routes consult the token's role too, so a token withadmin:*scope butauditorrole gets blocked from operational writes. Tokens issued without a role keep pre-slice-3 scope-only behaviour (back-compat). Mint guard: a creator can only issue tokens at or below their own role — no privilege escalation via token issuance - Self-service password change — every admin user can rotate their own password via My Account (
/ui/my-account). Requires the current password as a liveness check; new password must differ from the current and be ≥12 chars. LegacyADMIN_API_KEYactors get a clear 409 directing them to rotate via.env. Audit row ispassword_changed_selfwith no value content - External secret management (HashiCorp Vault + CyberArk CCP/AIM + Azure Key Vault + AWS Secrets Manager + CyberArk Conjur) — replace plaintext credentials in
app_configwith references to your secret store. Any secret-typed config row whose value isvault://<path>[#<field>],ccp://[<safe>/]<object>,azurekv://<vault>/<secret>,awssm://<secret-id>[#<field>], orconjur://<identifier>[#<field>]is resolved at read time via the configured backend. Plain string values keep working unchanged so partial migrations are safe. Process-local TTL cache (default 60s) avoids hammering the backend on every config read. Allis_secret=trueconfig keys flow through the resolver — AD bind, Entra client secret, SMTP password, vSphere/XenServer/SCCM passwords, Teams webhook, Splunk HEC token, Sentinel shared key + Logs Ingestion SPN secret, generic-webhook HMAC secret, Conjur api_key, etc. UI shows reference-shaped values in clear (the path isn't sensitive) so admins can see which store entry each row points to. Vault auth: static token (legacy / lab), AppRole (role_id + secret_id), or Kubernetes (projected service-account JWT) — short-lived tokens cached with auto-refresh before lease expiry. CCP auth: AppID + IP allow-list or optional mTLS via configured client cert (Settings UI offers a guided cert + key file-upload form, with PEM-shape validation that catches encrypted keys / swapped files before the resolver). Azure KV auth: Azure AD service principal — independent of the Entra ID SSO config so the KV SPN carries minimum-necessaryKey Vault Secrets Useraccess. AWS Secrets Manager auth: static IAM keys (long-lived or STS-issued temporaries) or native AssumeRole — ip·Solis itself callssts:AssumeRolewith a bootstrap IAM identity and refreshes the derived session credentials automatically beforeExpiration. Conjur auth: host API-key login with cached short-lived access tokens (works against on-prem Conjur and Conjur Cloud). One-shot bulk-migration tool at Settings → Compliance → External Secret Backend → Migrate plaintext secrets to backend: walks everyis_secret=truerow, pushes the plaintext value to the active backend, and replaces the row with the matching reference. Dry-run preview + per-row report; CCP rejected with a clear error since CyberArk Safes are managed via PVWA, not the AIM API - HA Celery Beat scheduler — drop-in
celery-redbeatswap moves the schedule into Redis with a Lua-script distributed lock. Rundocker compose up -d --scale beat=Nand only the lock-holder dispatches; failover from a hard kill of the leader takes ~13 seconds with the tuned 30-second lock TTL + 30-second non-leader poll. Schedule survives restart since it lives in Redis. No application changes — the existingapp.conf.beat_scheduledict is ingested by RedBeat on first boot - Full audit attribution — every audit row records who drove the change, not just what happened. Admin endpoints record the credential (
token:<name>/admin:session:<user>:<role>/admin:legacy_key); portal mutations record the Entra-resolved user (portal:user:<email>) orportal:anonymouswhen SSO is disabled; signed-token approvals recordapi:approval_token (approver:<email>). The same attribution flows into the order, asset, asset-type, approval, and delegation audit rows so any change can be traced back to the specific credential that triggered it - Field-level data classification — tag each asset attribute as
internal,pii,phi, orpci; the portal renders matching warning badges next to sensitive fields when requesters fill them in, and the classification flows into the audit-log snapshot for downstream retention queries
- Cost / chargeback per asset definition — set
monthly_cost,currency, andcost_centeron each definition; the Cost Report page aggregates active orders into projected monthly spend per cost center with CSV export - AD-driven consumer breakdown — at order creation we snapshot the requester's AD attributes (
department,cost_center,company,employeeID,title; attribute names configurable in Settings) onto each order, so the Cost Report can also slice spend by consuming team / department, and the per-order CSV carries every requester's full HR identity for spreadsheet pivots. Snapshot runs on every creation path — portal, publicPOST /orders/, and the ServiceNow webhook — so externally-driven orders feed the same consumer-side rows - Per-order cost projection — the portal's order detail page renders the projected total (
monthly_cost × months_requested) in the Access & Duration card whenever the asset definition is priced; users see what their request will cost before / after approval - Cost-threshold alerts — set monthly spend ceilings per
(cost_center, currency)on the Cost Report page; a daily Beat task at 04:00 emails the configured recipients (and posts a Teams card whenteams.mode=enabled) on breach. Hysteresis viacost.threshold_alert_quiet_hours(default 24h) suppresses repeats; edits to a threshold clear the alert clock so the next breach re-alerts immediately. Provider totals cards on the report flag breaches in red so the dashboard reads at a glance - Historical view — daily Beat task at 02:00 captures all three cost-report views into
cost_report_snapshots. The Cost Report page gets an As of date picker that reads from the snapshot table for past dates (falls back to live data when no snapshot exists). Retention configurable viacost.snapshot_retention_days(default 365) - FX conversion — admins set a canonical reporting currency (
cost.fx.canonical, defaultEUR) and a static rate map (cost.fx.rates); the Cost Report page gets a Show in currency selector that converts mixed-currency totals via cross-rates so summary cards collapse to a single figure per cost center. Currencies without configured rates surface in a meta banner
- Dashboard with live pool status tiles (auto-refreshing HTMX fragments)
- Setup checklist on the dashboard — auto-derived from current DB state, separates "essential" from "recommended", each pending item links to the relevant config page; collapses once everything is done and stays out of the way until something changes
- Pool capacity warnings on the dashboard — pools at ≥80% fill (warning, amber) or ≥95% (critical, red) surface in a banner above the status tiles, listed by severity with one-click links to the affected asset definition or pool view
- Full asset type configuration (categories, attributes, automation strategy, approvals)
- Runbook management with drag-and-drop step ordering (mouse via the
☰handle on each row, plus ↑ / ↓ buttons for keyboard accessibility) - Email template editor with variable placeholders (per-action templates)
- Central settings for AD, SMTP, vSphere, XenServer, SCCM, Entra ID
- Configurable app branding (title, logo, logo position/size)
- Global variables available to runbooks and scripts
- Audit log viewer and order change log
- Asset pool management with bulk import
- Session-based login (plus
X-Admin-Keyheader for API access)
- Active Directory / LDAP -- user validation, manager lookup, group membership
- OpenID Connect IdP -- portal SSO via any compliant provider (Entra ID, Okta, Ping, Google, Keycloak, …)
- vSphere / XenServer / XCP-ng -- VM lifecycle operations via PowerShell
- SCCM -- task sequence triggers for OS deployment
- SMTP -- transactional email notifications
- ServiceNow -- inbound webhook for order dispatch
+------------------+
| Nginx (SSL) |
+--------+---------+
|
+--------+---------+
| FastAPI (API) |
| Admin UI |
| Portal |
+----+--------+----+
| |
+--------+--+ +--+--------+
| PostgreSQL | | Redis |
+------------+ +-----+-----+
|
+------------+------------+
| | |
+----+----+ +----+----+ +-----+-----+
| Worker | | Beat | | Flower |
| (Celery)| |(Sched.) | | (Monitor) |
+----+----+ +---------+ +-----------+
|
+----------+----------+
| | |
+---+---+ +---+---+ +---+---+
|vSphere| | SCCM | | AD |
+-------+ +-------+ +-------+
| Layer | Technology |
|---|---|
| API / Dispatcher | FastAPI (Python 3.12) |
| Workflow Engine | Celery + Redis |
| Scheduling | Celery Beat |
| Database | PostgreSQL 16 (SQLAlchemy + Alembic) |
| Frontend | HTMX + Jinja2 + Tailwind CSS |
| Portal Authentication | OIDC SSO — any compliant IdP (Entra ID, Okta, …) + on-prem LDAP |
| VM Operations | PowerShell / PowerCLI |
| Directory Services | Active Directory (msldap) |
| Deployment | Docker Compose |
git clone https://github.com/XenPool/ipsolis.git
cd ipsolis
cp .env.example .env
# Edit .env -- set database credentials and API secrets
# Build and start the full stack in the background
docker compose up -d --build
# Apply database migrations (required on first install AND after every upgrade)
docker compose exec api alembic upgrade headThe api service does not auto-run migrations at startup, so this step is required on first install. The same command is also how you upgrade the schema after pulling new code.
| Service | URL |
|---|---|
| Self-Service Portal | http://localhost:8000/portal |
| Admin UI | http://localhost:8000/ui/ |
| API Docs (Swagger) | http://localhost:8000/docs |
| Celery Flower | http://localhost:5555 |
On first visit to /ui/, the admin login renders a "Create first
administrator" form (RBAC first-run setup). Create the initial
superadmin, then configure external systems (Active Directory, SMTP,
vSphere, Entra ID SSO) through the Admin UI at /ui/settings. The
dashboard's setup checklist tracks what's left to configure.
The repository ships two compose files. The main one is the full stack (postgres / redis / api / worker / beat / flower); the second is a small overlay (just nginx) that adds TLS termination in front of the api.
| Mode | Command | When |
|---|---|---|
| Direct | docker compose up -d |
Dev. API on http://localhost:8000/, no proxy. |
| TLS-fronted | docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d |
Pre-live / prod. Nginx handles TLS using nginx/ssl/cert.pem + nginx/ssl/key.pem; reach the app at https://<your-host>/. |
The overlay is purely additive — same database, same migrations, same image tags. Switch between modes by stopping (down) and starting with the alternate command.
See the full deployment guide: docs/DEPLOYMENT.md
High-availability deployment patterns (multi-replica api / worker, Postgres standby + failover) live in section 12 of docs/DEPLOYMENT.md. Grafana dashboard + Prometheus alerts in docs/grafana/; ops runbooks in docs/runbooks/.
Summary:
- Provision a Linux server with Docker
- Configure
.envwith secure credentials - Set up SSL certificates (internal CA, mkcert, or Let's Encrypt)
- Start with
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d - Run migrations:
docker compose exec -T api alembic upgrade head - Configure AD, SMTP, and Entra ID through the Admin UI
api/
app/
models/ SQLAlchemy ORM models
routes/ FastAPI routers (admin, admin_auth, admin_modules,
admin_runbooks, admin_standalone_runbooks,
admin_maintenance, admin_users, admin_self,
admin_api_tokens, admin_license, admin_setup,
admin_seed_export, admin_cost_report,
admin_approval_delegations, approvals_external,
portal, portal_delegations, auth, orders,
metrics, ui)
schemas/ Pydantic request/response schemas
templates/ Jinja2 templates (Admin UI + Portal + admin login)
static/ Static JS/CSS assets served by FastAPI
utils/ AD (msldap), capacity, MSAL, admin auth, PS param parser,
RBAC, password (PBKDF2), api_tokens, secrets resolver
(Vault / CCP), approval token signer + decision helper,
SoD detection, classification, metrics, tracing, audit
alembic/
versions/ Database migrations (0001 ... 0093, head bumps with each
feature slice — see `docker compose exec api alembic
history` for the full chain)
worker/
tasks/
modules/ Atomic workflow modules (pool_manager, vsphere,
active_directory, notifications, target_executor,
teams_notify, maintenance, config_reader, secrets,
audit_helper, step_helper, registry)
workflows/ Orchestration (dynamic_runner, standalone_runner,
ps_module_installer, license_check,
audit_retention, api_token_purge,
approval_reminders, approval_auto_decline,
cost_threshold_alerter, cost_report_snapshot,
update_checker)
tracing.py OpenTelemetry setup for the worker side
scripts/
modules/ Per-category PowerShell script modules synced from the DB
(ad / sccm / sql / test / vmware / xenserver) — seed
material only, runtime reads from the DB
runbooks/ Standalone runbook JSON snapshots (seed material)
tools/
validate_locales.py Portal i18n JSON key-tree validator
locales/ Portal i18n (de/en/es/fr/it JSON)
nginx/ Reverse-proxy + TLS config (production overlay)
backups/ Persisted DB dumps (bind-mounted into api + worker)
licenses/ Signed `.lic` license file (read-write)
docs/
DEPLOYMENT.md Production deployment guide
grafana/ Ready-to-import dashboard + Prometheus alerts
runbooks/ Operational runbooks (e.g. compose project rename)
This software is designed for on-premises deployment. All data stays within your infrastructure.
| Data | Where | Purpose |
|---|---|---|
| Email address, display name | orders, order_approvals |
Order processing, notifications |
| sAMAccountName | orders (RDP/admin user lists) |
Access provisioning |
| Manager relationship | Queried live from AD, not stored | Approval workflow |
| Action history | audit_log |
Accountability and compliance |
| Session token | Browser cookie (xp_session) |
Authentication |
- No data leaves your network (zero telemetry, no cloud dependencies)
- Tamper-evident audit log (BEFORE-statement triggers block DELETE / UPDATE / TRUNCATE outside a documented bypass)
- Per-classification audit retention windows (PII / PHI / PCI configurable independently of the global window)
- Five-tier admin RBAC with per-asset-type ACL grants and separation-of-duties enforcement
- OIDC SSO for the portal — any compliant IdP (no password storage on the portal side)
- Admin password storage uses PBKDF2-SHA256 (600k iterations, OWASP-2023)
- External secret management (HashiCorp Vault / CyberArk CCP / Azure Key Vault / AWS Secrets Manager / CyberArk Conjur) replaces plaintext credentials in
app_config; bulk-migration tool to push existing plaintext to the chosen backend in one pass - All traffic encrypted via HTTPS (nginx TLS termination)
- Field-level data classification badges so requesters know which fields are sensitive before submitting
- Order-history retention with automatic cleanup
- User data export (data portability)
- User data anonymization/deletion
- Configurable session cookie hardening (production defaults)
- Privacy notice integration for the portal
Operators are responsible for maintaining their own records of processing activities and providing appropriate privacy notices to their users as required by applicable regulations.
Coming soon
Shipped
- Multi-language support (i18n) — portal available in DE / EN / ES / FR / IT
- Dashboard with live pool status tiles, setup checklist, pool capacity warnings
- Maintenance UI (backups, health probes, queue inspection)
- PowerShell module management (Gallery + manual upload, with Linux compatibility flag)
- Standalone runbooks (ad-hoc + cron-scheduled)
- Per-classification audit-log retention with tamper-evident triggers
- Role-based admin access — five-tier ladder with per-asset-type ACL grants and SoD enforcement
- API token management for external integrations (per-token scopes + role binding)
- Audit log viewer + SIEM streaming (Splunk HEC / Microsoft Sentinel / generic webhook)
- OpenTelemetry tracing (api + Celery worker) + Prometheus metrics + Grafana dashboard
- External secret management (HashiCorp Vault + CyberArk CCP/AIM + Azure Key Vault + AWS Secrets Manager + CyberArk Conjur)
- HA Beat scheduler (celery-redbeat with Redis-backed distributed lock)
- Cost / chargeback per asset definition with provider + consumer (AD-driven) views
- Approval reminders, escalation, delegation (admin + portal self-service), and auto-decline
Open
- Access certification campaigns — slice 1 + slice 2 (admin CRUD + kickoff + manager portal page + signed-token review URLs + email reminders / overdue / escalation + auto-revoke on overdue)
- HR feed + SCIM 2.0 — slice 1 (unified leaver flow, HR webhook with Workday/SAP/MS-Graph adapters, SCIM 2.0 leaver subset)
- HR feed + SCIM — slice 2 (full SCIM filter grammar, /Groups shim, bulk operations)
- Cost report: threshold alerting (per-
cost_center/currency monthly limits with email + optional Teams-card alerts and hysteresis) - Cost report: historical view via daily snapshot table +
?as_of=query - Cost report: FX conversion across mixed-currency cost centers via configurable rate map
- Data retention and automatic cleanup (order history)
- User data export and deletion (GDPR-style data portability)
- Optional Sentry integration for error tracking
- Terraform provider for asset definition configuration
ip·Solis is source-available under the XenPool Commercial Source License v1.0 — see the Licensing section above for the summary (free productive use up to 25 active users, plus private and 30-day evaluation use; production use with more than 25 active users requires a purchased volume-band license).
See LICENSE for the full license text. Get a commercial license at the ip·Solis license shop.
This project bundles third-party open-source components under their own licenses (MIT, BSD, Apache-2.0, LGPL, and others). See THIRD-PARTY-LICENSES.md for the complete list.
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
- Bug reports & features: GitHub Issues
- Commercial support: Contact sales@xenpool.de for SLA-backed support contracts and consulting
Built by XenPool GmbH -- born from 30 years of datacenter operations.