Security architecture
How eworks.cloud protects workspaces, data and models, layer by layer.
Overview: defense in depth
No single control is trusted on its own. Every request crosses at least five independent layers, and each layer fails closed.
| Layer | Primary control | Fails closed by |
|---|---|---|
| Edge | WAF, DDoS mitigation, TLS 1.3 termination | Dropping the connection |
| Identity | SSO federation + MFA | Rejecting the session |
| Authorization | RBAC + ABAC + tenant isolation | Returning an empty result set |
| Data | AES-256 at rest, field-level encryption | Refusing to decrypt |
| Observability | Append-only audit trail, anomaly detection | Alerting and rate-limiting |
Design rules the platform holds itself to:
- Deny by default. A new table, endpoint or tool is unreachable until an explicit policy grants access.
- Least privilege. Service credentials are scoped to a single capability and a single environment.
- No implicit trust between tenants. Tenant identity is derived from the verified token, never from a request parameter.
- Everything privileged is logged. If an action can change access, data or configuration, it produces an audit event.
Authentication
SSO federation
Workspaces can federate with any SAML 2.0 or OIDC identity provider — Entra ID, Okta, Google Workspace, Keycloak, JumpCloud. Domain-verified workspaces can force federated login and disable password sign-in entirely.
- Assertions are validated for signature, audience, issuer, and clock skew (±120 s).
NameID/subis the stable external identity; email changes do not create a new account.- Just-in-time provisioning creates the member on first login with the default workspace role.
- SCIM 2.0 keeps membership and deprovisioning in sync.
Multi-factor authentication
| Factor | Notes |
|---|---|
| Passkey (WebAuthn) | Phishing-resistant, recommended default |
| YubiKey / U2F security key | Hardware-backed, supports FIPS keys |
| TOTP | RFC 6238, 30-second window, one-step drift tolerance |
| Recovery codes | 10 single-use codes, shown once, hashed at rest |
SMS is intentionally not offered. Admins can require MFA for all members, or only for privileged roles.
Session management
- Access tokens are short-lived JWTs (15 minutes) signed with rotating RS256 keys.
- Refresh tokens are rotating and single-use; reuse of a consumed refresh token revokes the whole session family.
- Idle timeout 8 hours, absolute timeout 30 days, both configurable per workspace.
- Admins can revoke any individual session or all sessions for a member from e.identity.
Verify a token against the published JWKS:
curl -s https://id.eworks.cloud/.well-known/jwks.json | jq '.keys[].kid'from jwt import PyJWKClient
import jwt
jwks = PyJWKClient("https://id.eworks.cloud/.well-known/jwks.json")
key = jwks.get_signing_key_from_jwt(token).key
claims = jwt.decode(
token,
key,
algorithms=["RS256"],
audience="eworks-api",
issuer="https://id.eworks.cloud",
)
print(claims["sub"], claims["workspace_id"], claims["role"])Authorization
Role-based access control
| Role | Read data | Write data | Manage members | Billing & security settings |
|---|---|---|---|---|
| Admin | Yes | Yes | Yes | Yes |
| Member | Yes | Yes | No | No |
| Viewer | Yes | No | No | No |
Roles are stored in a dedicated user_roles table, never on the profile record, and are always evaluated server-side. A client-supplied role claim is ignored.
Attribute-based access control
On top of roles, policies evaluate attributes carried by the request:
workspace_id— hard tenant boundary.department— department tags on documents, agents and chat sessions restrict visibility to matching members.classification—public,internal,confidential,restricted. Restricted records require an explicit grant plus MFA within the last hour.environment— sandbox credentials can never read production records.
Tenant isolation
Every tenant-scoped table enforces row-level security keyed on the workspace claim of the verified token. Object storage paths are prefixed with the workspace ID and signed URLs expire in 15 minutes. Vector indexes are partitioned per workspace so retrieval can never cross a tenant boundary.
Data encryption
| State | Control |
|---|---|
| At rest | AES-256-GCM on all database volumes, object storage and backups, with managed KMS keys and automatic annual rotation |
| In transit | TLS 1.3 externally; mutual TLS between internal services |
| Field level | Envelope encryption for API credentials, connector secrets, and any field tagged restricted |
| Backups | Encrypted with a separate key, restore requires two-person approval |
Field-level ciphertext is never returned by the API — only a masked reference such as sk_live_••••4f2a — and the plaintext is decrypted at point of use inside the service that needs it.
Network security
- Application, database and worker subnets are separated inside a private network; databases have no public route.
- Security groups allow only the specific ports each tier needs; egress is restricted to an allowlist of model and connector endpoints.
- A managed WAF applies OWASP core rules, bot filtering and per-path request limits at the edge.
- Volumetric DDoS mitigation is always on at the edge, with automatic challenge escalation.
- Administrative interfaces support IP allowlisting per workspace.
API security
- Bearer tokens. OAuth 2.0 JWT access tokens with scoped claims; the
Authorization: Bearerheader is the only accepted credential for user-context calls. - Rate limiting. Per token, per workspace and per IP. Defaults: 600 requests/minute per token, 60 model completions/minute per workspace. Exceeding returns
429withRetry-After. - Request signing. Webhooks and server-to-server integrations are signed with HMAC-SHA256 over the raw body plus a timestamp; requests older than 5 minutes are rejected.
- API key rotation. Keys are issued with an expiry (90 days by default), support overlapping dual-key rotation, and are revocable instantly.
Verify a webhook signature:
import hmac, hashlib, time
def verify(body: bytes, header: str, secret: str) -> bool:
ts, sig = header.split(",", 1)
ts = ts.removeprefix("t=")
sig = sig.removeprefix("v1=")
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)Logging and monitoring
- Application, access and security logs stream to CloudWatch log groups with a 400-day hot window; audit events are additionally written to the immutable 7-year store.
- Anomaly detection watches authentication failures, unusual geographies, bulk exports, privilege changes and abnormal token spend.
- Alerting thresholds:
| Signal | Threshold | Action |
|---|---|---|
| Failed authentications, single account | > 5 in 10 minutes | Lock account, page on-call |
| Failed authentications, workspace-wide | > 50 in 10 minutes | Security incident opened |
| Bulk data access | > 1,000 records in 5 minutes | Alert workspace admins |
| Privilege escalation | Any role change to Admin | Immediate notification to all admins |
| Impossible travel | Two logins > 800 km/h apart | Session challenge + alert |
Note: alerting thresholds are workspace-configurable in e.dash. Lowering them does not reduce what is logged — logging is always complete.