Cassanova is a web-based management interface for Apache Cassandra. Built with Python (FastAPI), it provides cluster monitoring, data exploration, and configuration-driven role-based access control.
- Stateless Authentication: Configuration-driven JWT authentication requiring no database.
- Granular Permissions: Role definitions with specific permissions (e.g.,
cluster:view,tools:cqlsh,data:write). - Access Control: Protected API endpoints and role-aware UI elements.
- Auditing: Centralized login flow with secure session handling.
- Filtering: Filter data by Partition Keys, Clustering Keys, or specific columns.
- Cross-View Integration: Filters persist across Data Layout graphs and List views.
- Data Management: Form-based row insertion, aware of table schema.
- Topology Visualization: Graphs showing Token Ring and Schema relationships.
- Schema Management: UI for creating and modifying Keyspaces and Tables.
- CQL Console: Web-based terminal with history and syntax highlighting.
- Query Tracing: List view for analyzing query performance and latency.
- Process Management: Web interfaces for
sstabledump,nodetool, andcassandra-stress.
- Cassandra Roles: View, create, modify, and delete Cassandra roles directly from the UI.
- Permission Grants: Grant and revoke per-resource permissions (SELECT, MODIFY, ALTER, etc.) with a visual access map.
- Login Filter: Toggle between all roles and login-enabled roles only.
- Access required:
roles:viewpermission.
- Side-by-side diff: Compare keyspace and table schemas across any two clusters.
- Visual tree: Hierarchical diff view with
Identical,Different,Only A, andOnly Bstatus badges. - Schema browser: Select the same cluster on both sides to browse its full schema as a tree.
- Always available: Accessible regardless of cluster count.
- Cluster Monitoring: Node status, VNode distribution, and Token Range maps.
- Multi-Cluster Support: Manage multiple Cassandra clusters from a single dashboard.
- Theming: 11 built-in themes with a quick-switch palette in the user menu.
- Navigation: Top bar with breadcrumbs, global search across clusters/keyspaces/tables, and keyboard shortcut (Ctrl+K).
To run Cassanova using Docker:
docker pull poortuna/cassanova:1.16.0Create a cassanova.json file. This includes the auth section:
{
"app_config": {
"host": "0.0.0.0",
"port": 8080,
"routers": ["cassanova_ui_router", "cassanova_api_router"]
},
"auth": {
"enabled": true,
"secret_key": "CHANGE_THIS_SECRET_KEY",
"algorithm": "HS256",
"session_expire_minutes": 120,
"users": [
{
"username": "admin",
"password": "admin_password",
"roles": ["admin"]
},
{
"username": "viewer",
"password": "view_only_password",
"roles": ["viewer"]
}
],
"roles": [
{ "name": "admin", "permissions": ["*"] },
{ "name": "viewer", "permissions": ["cluster:view", "roles:view"] }
]
},
"clusters": {
"proda": {
"contact_points": ["10.0.0.1", "10.0.0.2"],
"port": 9042
}
},
"k8s": {
"enabled": true,
"kubeconfig": "/etc/cassanova/kubeconfig",
"contexts": null,
"namespace": "default",
"cluster_include": [],
"cluster_exclude": [],
"suffix": "-service",
"periodic_discovery_enabled": true,
"discovery_interval_seconds": 60,
"external_only": false,
"stale_threshold": 3
}
}Optional: Enable TLS by adding
"tls": {"enabled": true, "cert_file": "/path/to/cert.crt", "key_file": "/path/to/key.key"}underapp_config.
docker run -p 8080:8080 \
-e CASSANOVA_CONFIG_PATH=/config/cassanova.json \
-v $(pwd)/cassanova.json:/config/cassanova.json \
poortuna/cassanova:1.16.0Note: Ensure your Cassandra nodes are reachable from within the container.
Open http://localhost:8080. Log in with the credentials defined in your JSON config.
Cassanova is configured via CASSANOVA_CONFIG_PATH.
Most UI settings support hot-reloading. Auth changes require a restart.
Cassanova can discover K8ssandraCluster instances from a Kubernetes cluster.
| Setting | Description | Default |
|---|---|---|
k8s.enabled |
Enable K8s discovery on startup | false |
k8s.kubeconfig |
Path to kubeconfig file | null |
k8s.namespace |
Namespace to scan (or all if null) | null |
k8s.cluster_include |
Glob patterns matched against K8ssandraCluster name; empty = match all |
[] |
k8s.cluster_exclude |
Glob patterns matched against K8ssandraCluster name; exclude wins over include |
[] |
k8s.suffix |
Service name suffix (e.g., -metallb) |
-service |
k8s.periodic_discovery_enabled |
Enable periodic background scans | false |
k8s.discovery_interval_seconds |
Interval for periodic scans in seconds | 60 |
k8s.external_only |
Only accept LoadBalancer IPs and externalIPs; skip ClusterIP and DNS |
false |
k8s.stale_threshold |
Consecutive missed scans before a discovered cluster is evicted | 3 |
Providing a kubeconfig (Helm chart): when Cassanova runs outside the target cluster, point it at a kubeconfig three ways:
| Chart value | Behavior |
|---|---|
| (unset) | Uses the pod ServiceAccount (in-cluster config). Default. |
config.k8s.kubeconfig |
Inline kubeconfig content. Rendered into a Secret, mounted at /etc/cassanova/kubeconfig/config, and k8s.kubeconfig is pointed there automatically. Good for ArgoCD external values repos. |
config.k8s.kubeconfigSecret |
Name of a pre-existing Secret holding the kubeconfig under key kubeconfig. Mounted the same way. Takes precedence over config.k8s.kubeconfig. |
# Inline (e.g. from an ArgoCD values repo)
config:
k8s:
enabled: true
kubeconfig: |
apiVersion: v1
kind: Config
clusters: [...]
# Or reference an existing secret
config:
k8s:
enabled: true
kubeconfigSecret: my-cluster-kubeconfigMechanism:
- Cassanova scans for
K8ssandraClusterCRs. - Fetches credentials from the
<cluster>-superuserSecret. - Identifies Services matching
<cluster>-<dc><suffix>to extract contact points. - Merges discovered clusters into the configuration.
- Clusters not seen for
stale_thresholdconsecutive scans are evicted and their sessions closed.
external_onlymode: When enabled, only LoadBalancer ingress IPs andexternalIPsare accepted as contact points. Useful when Cassanova runs outside the cluster (e.g., external monitoring host) and must reach Cassandra via MetalLB or cloud load balancers rather than internal DNS.
Cluster filtering: Use
cluster_includeandcluster_excludewith fnmatch glob patterns (*,?,[seq]) to control whichK8ssandraClusternames are imported. Exclude takes precedence over include. An emptycluster_includematches all clusters.
The Helm chart supports OpenShift natively without requiring anyuid or privileged SCC grants.
- Pod runs as non-root (
runAsNonRoot: true). OpenShift injects a namespace-allocated UID automatically. allowPrivilegeEscalation: falseandcapabilities.drop: [ALL]satisfy therestrictedSCC out of the box.- A
ClusterRoleandClusterRoleBindingare created automatically by the chart (RBAC enabled by default). No manualoccommands needed.
The chart also includes an optional Route resource for OpenShift ingress (route.enabled: true).
Cassanova exposes a cluster inventory endpoint for operators and automation.
Requires permission: cluster:admin
| Endpoint | Method | Description |
|---|---|---|
/api/v1/admin/clusters |
GET |
List all registered clusters with provenance metadata |
Query parameters:
| Parameter | Default | Description |
|---|---|---|
expose_credentials |
true |
Include plaintext credentials in the response. Set to false to mask them. |
Example response:
[
{
"name": "my-cluster",
"source": "k8s",
"context": null,
"contact_points": ["10.0.0.249"],
"port": 9042,
"credentials": { "username": "my-cluster-superuser", "password": "..." },
"jmx_credentials": null,
"has_credentials": true,
"has_jmx_credentials": false,
"has_additional_kwargs": false,
"last_seen": "2026-05-09T20:19:24Z",
"miss_count": 0
}
]Includes a dashboard to handle Cassandra node failures in Kubernetes (e.g., OpenShift with LVMS).
| Setting | Description | Default |
|---|---|---|
k8s.node_recovery.enabled |
Enable the node recovery service | false |
Recovery Workflow:
- Detect: Queries for
Pendingpods with Volume Node Affinity issues. - Review: Administrator approves the recovery.
- Recover: Creates a
K8ssandraTask(replacenode) to fix the pod.
Supports TLS encryption.
| Setting | Description | Default |
|---|---|---|
app_config.tls.enabled |
Enable HTTPS/TLS | false |
app_config.tls.cert_file |
Path to SSL certificate (.crt/.pem) | Required if enabled |
app_config.tls.key_file |
Path to private key (.key/.pem) | Required if enabled |
app_config.tls.ca_bundle |
Optional CA certificate chain | null |
app_config.tls.min_tls_version |
Minimum TLS version (TLSv1_2 or TLSv1_3) |
TLSv1_2 |
app_config.tls.enforce_https |
Redirect HTTP → HTTPS (301) | true |
app_config.tls.hsts_enabled |
Enable HSTS security headers | true |
app_config.tls.hsts_max_age |
HSTS max-age in seconds | 31536000 (1 year) |
app_config.tls.hsts_include_subdomains |
Apply HSTS to subdomains | false |
Security Features:
- Cipher Suites - ECDHE/CHACHA20/AES-GCM ciphers
- Protocols - TLS 1.2+ enforcement
- HSTS - Prevents downgrade attacks
- Secure Cookies - Session cookies marked
SecureandSameSite=Lax - Redirects - Forces HTTPS connections
Supports LDAP/AD integration for centralized user management.
| Setting | Description | Default |
|---|---|---|
auth.ldap.enabled |
Enable LDAP auth | false |
auth.ldap.server_uri |
LDAP URI (ldap:// or ldaps://) | ldap://localhost:389 |
auth.ldap.base_dn |
Base DN for user/group search | dc=example,dc=com |
auth.ldap.bind_dn |
Service account DN (null for anonymous) | null |
auth.ldap.role_mapping |
Map LDAP groups to Cassanova roles | {} |
Role Mapping Strategies:
- Group Name: Matches the
cn(or configured attribute) of the group. e.g.,"Domain Admins": ["admin"]. - Exact DN: Matches the full Distinguished Name of the group. e.g.,
"cn=group,dc=com": ["admin"]. - Branch/Suffix: Matches any group located under a specific OU. e.g.,
"ou=Admins,dc=com": ["admin"].
Example Config:
"auth": {
"enabled": true,
"ldap": {
"enabled": true,
"server_uri": "ldaps://ad.example.com:636",
"bind_dn": "cn=svc-cassanova,ou=Users,dc=example,dc=com",
"bind_password": "secret_password",
"base_dn": "dc=example,dc=com",
"user_search_filter": "(sAMAccountName={username})",
"group_search_base": "ou=Groups,dc=example,dc=com",
"group_search_filter": "(member={user_dn})",
"role_mapping": {
"Domain Admins": ["admin"],
"Developers": ["viewer"],
"cn=SpecificGroup,ou=Groups,dc=example,dc=com": ["admin"],
"ou=NuclearBranch,dc=example,dc=com": ["admin"]
}
}
}To run locally (Linux/WSL recommended):
git clone https://github.com/poortuna/cassanova
cd cassanova
pip install uv
uv pip install -e ".[dev]"
# Set config path
export CASSANOVA_CONFIG_PATH=./cassanova.json
# Run Server
python -m cassanova.runruff check . # Lint
ruff format . # Format
mypy cassanova/ # Type check
pytest tests/ -vv # Run tests
pre-commit install # Set up git hooksCassanova is open source, licensed under the MIT License.



