Initial import — snapshot from admin host /srv/gosec/gsc-ops-api

This repo had no version control prior to this commit. The import is a
straight snapshot of the working tree at 2026-05-03; the deployed
binary on fihelvop01 was being rebuilt from this source via `make
build` + scp into place, with no upstream review path.

The snapshot already includes one in-flight fix made on 2026-05-03 to
internal/service/persona.go:GetSelfModel — the handler queried
`source` and `strength` columns plus an `is_active = true` filter on
persona.persona_commitments, none of which exist on that table (its
shape is session-bound commitments with `status`, `commitment_meta`,
etc.). The query returned a 500 every time SynapseHub bootstrapped a
persona's self-model, dropping the IdentityConstraints / Commitments /
ConscienceStandards layer from the assembled prompt. The patched
query reads existing columns only (commitment_text, commitment_type),
filters on `status='active'`, and synthesises Source="learned" /
Strength=1.0 to keep the SelfModel response shape stable for callers.

Verified live: `GET /api/v1/personas/70f7cfd9-.../self-model` now
returns 200 with `{identityConstraints:[],commitments:[],
conscienceStandards:[]}` instead of 500.

Future changes go through PRs against this repo — no more bin-only
deploys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude (gsc-ops-api init)
2026-05-03 20:06:02 +02:00
commit 3847eb2036
68 changed files with 12982 additions and 0 deletions

65
pkg/types/errors.go Normal file
View File

@@ -0,0 +1,65 @@
package types
import "fmt"
// Standard error codes for REST API responses
const (
CodeBadRequest = "BAD_REQUEST"
CodeUnauthorized = "UNAUTHORIZED"
CodeForbidden = "FORBIDDEN"
CodeNotFound = "NOT_FOUND"
CodeConflict = "CONFLICT"
CodeValidation = "VALIDATION_ERROR"
CodeInternal = "INTERNAL_ERROR"
CodeServiceUnavail = "SERVICE_UNAVAILABLE"
CodeTimeout = "TIMEOUT"
)
// APIError represents a structured REST API error
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
RequestID string `json:"requestId,omitempty"`
Detail string `json:"detail,omitempty"`
Status int `json:"-"`
}
func (e *APIError) Error() string {
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}
func NewBadRequest(msg string) *APIError {
return &APIError{Code: CodeBadRequest, Message: msg, Status: 400}
}
func NewUnauthorized(msg string) *APIError {
return &APIError{Code: CodeUnauthorized, Message: msg, Status: 401}
}
func NewForbidden(msg string) *APIError {
return &APIError{Code: CodeForbidden, Message: msg, Status: 403}
}
func NewNotFound(msg string) *APIError {
return &APIError{Code: CodeNotFound, Message: msg, Status: 404}
}
func NewConflict(msg string) *APIError {
return &APIError{Code: CodeConflict, Message: msg, Status: 409}
}
func NewValidation(msg string) *APIError {
return &APIError{Code: CodeValidation, Message: msg, Status: 422}
}
func NewInternal(msg string) *APIError {
return &APIError{Code: CodeInternal, Message: msg, Status: 500}
}
func NewServiceUnavailable(msg string) *APIError {
return &APIError{Code: CodeServiceUnavail, Message: msg, Status: 503}
}
func NewTimeout(msg string) *APIError {
return &APIError{Code: CodeTimeout, Message: msg, Status: 504}
}

396
pkg/types/models.go Normal file
View File

@@ -0,0 +1,396 @@
package types
import (
"time"
"github.com/google/uuid"
)
// LDAPUser represents a FreeIPA user
type LDAPUser struct {
UID string `json:"uid"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
DisplayName string `json:"displayName"`
Email string `json:"email"`
Phone string `json:"phone,omitempty"`
Title string `json:"title,omitempty"`
Disabled bool `json:"disabled"`
Groups []string `json:"groups,omitempty"`
Shell string `json:"shell,omitempty"`
HomeDir string `json:"homeDir,omitempty"`
// Extended GoSec service attributes grouped by domain
Services map[string]map[string]interface{} `json:"services,omitempty"`
ObjectClasses []string `json:"objectClasses,omitempty"`
}
// LDAPUserCreate is the request body for creating a user
type LDAPUserCreate struct {
UID string `json:"uid" validate:"required"`
FirstName string `json:"firstName" validate:"required"`
LastName string `json:"lastName" validate:"required"`
Email string `json:"email"`
Password string `json:"password,omitempty"`
Phone string `json:"phone,omitempty"`
Title string `json:"title,omitempty"`
Shell string `json:"shell,omitempty"`
Services map[string]map[string]interface{} `json:"services,omitempty"`
}
// LDAPUserUpdate is the request body for updating a user
type LDAPUserUpdate struct {
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
Email *string `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"`
Title *string `json:"title,omitempty"`
Shell *string `json:"shell,omitempty"`
Disabled *bool `json:"disabled,omitempty"`
Services map[string]map[string]interface{} `json:"services,omitempty"`
}
// LDAPGroup represents a FreeIPA group
type LDAPGroup struct {
CN string `json:"cn"`
Description string `json:"description,omitempty"`
Members []string `json:"members,omitempty"`
GIDNumber string `json:"gidNumber,omitempty"`
}
// LDAPGroupCreate is the request body for creating a group
type LDAPGroupCreate struct {
CN string `json:"cn" validate:"required"`
Description string `json:"description,omitempty"`
}
// LDAPGroupUpdate is the request body for updating a group
type LDAPGroupUpdate struct {
Description *string `json:"description,omitempty"`
}
// LDAPGroupMemberAdd is the request body for adding members
type LDAPGroupMemberAdd struct {
Members []string `json:"members" validate:"required"`
}
// PasswordReset is the request body for resetting a password
type PasswordReset struct {
NewPassword string `json:"newPassword" validate:"required"`
}
// DNSZone represents a PowerDNS zone
type DNSZone struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
DNSSec bool `json:"dnssec"`
Serial int64 `json:"serial"`
NotifiedSerial int64 `json:"notifiedSerial"`
Records []DNSRecord `json:"records,omitempty"`
SOAEdit string `json:"soaEdit,omitempty"`
SOAEditAPI string `json:"soaEditApi,omitempty"`
}
// DNSRecord represents a DNS resource record set
type DNSRecord struct {
Name string `json:"name"`
Type string `json:"type"`
TTL int `json:"ttl"`
Records []DNSRecordEntry `json:"records"`
Comments []DNSRecordComment `json:"comments,omitempty"`
}
// DNSRecordEntry represents a single record within an RRset
type DNSRecordEntry struct {
Content string `json:"content"`
Disabled bool `json:"disabled"`
}
// DNSRecordComment represents a comment on an RRset
type DNSRecordComment struct {
Content string `json:"content"`
Account string `json:"account"`
ModifiedAt int64 `json:"modified_at"`
}
// DNSZoneCreate is the request body for creating a zone
type DNSZoneCreate struct {
Name string `json:"name" validate:"required"`
Kind string `json:"kind"`
Nameservers []string `json:"nameservers"`
Masters []string `json:"masters,omitempty"`
}
// DNSZoneUpdate is the request body for updating zone metadata
type DNSZoneUpdate struct {
Kind *string `json:"kind,omitempty"`
Masters []string `json:"masters,omitempty"`
}
// DNSRecordChange is the request body for creating/updating/deleting records
type DNSRecordChange struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
TTL int `json:"ttl"`
ChangeType string `json:"changetype" validate:"required"`
Records []DNSRecordEntry `json:"records"`
}
// DomainSetup is the request body for orchestrated domain setup
type DomainSetup struct {
Domain string `json:"domain" validate:"required"`
MXHost string `json:"mxHost,omitempty"`
DKIMKey string `json:"dkimKey,omitempty"`
SPFIncludes []string `json:"spfIncludes,omitempty"`
}
// DomainVerify is the request body for domain verification
type DomainVerify struct {
Domain string `json:"domain" validate:"required"`
}
// DomainVerifyResult is the response for domain verification
type DomainVerifyResult struct {
Domain string `json:"domain"`
Results map[string]string `json:"results"`
AllOK bool `json:"allOk"`
}
// Tenant represents a database tenant record
type Tenant struct {
ID uuid.UUID `json:"id"`
CustomerID uuid.UUID `json:"customerId"`
Code string `json:"code"`
Name string `json:"name"`
DisplayName *string `json:"displayName,omitempty"`
Domain *string `json:"domain,omitempty"`
LogoURL *string `json:"logoUrl,omitempty"`
PrimaryColor *string `json:"primaryColor,omitempty"`
MaxUsers *int `json:"maxUsers,omitempty"`
MaxStorageGB *int `json:"maxStorageGb,omitempty"`
MaxRecordingHours *int `json:"maxRecordingHours,omitempty"`
IsActive *bool `json:"isActive"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// TenantCreate is the request body for creating a tenant
type TenantCreate struct {
CustomerID uuid.UUID `json:"customerId" validate:"required"`
Code string `json:"code" validate:"required"`
Name string `json:"name" validate:"required"`
DisplayName string `json:"displayName,omitempty"`
Domain string `json:"domain,omitempty"`
LogoURL string `json:"logoUrl,omitempty"`
PrimaryColor string `json:"primaryColor,omitempty"`
MaxUsers *int `json:"maxUsers,omitempty"`
MaxStorageGB *int `json:"maxStorageGb,omitempty"`
MaxRecordingHours *int `json:"maxRecordingHours,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// TenantUpdate is the request body for updating a tenant
type TenantUpdate struct {
Name *string `json:"name,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Domain *string `json:"domain,omitempty"`
LogoURL *string `json:"logoUrl,omitempty"`
PrimaryColor *string `json:"primaryColor,omitempty"`
MaxUsers *int `json:"maxUsers,omitempty"`
MaxStorageGB *int `json:"maxStorageGb,omitempty"`
MaxRecordingHours *int `json:"maxRecordingHours,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// DBUser represents a database user record
type DBUser struct {
ID uuid.UUID `json:"id"`
GscSID string `json:"gscsid"`
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Email *string `json:"email,omitempty"`
Timezone *string `json:"timezone,omitempty"`
Locale *string `json:"locale,omitempty"`
Status *string `json:"status,omitempty"`
LastLoginAt *time.Time `json:"lastLoginAt,omitempty"`
LastActivityAt *time.Time `json:"lastActivityAt,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// DBUserCreate is the request body for creating a user record
type DBUserCreate struct {
GscSID string `json:"gscsid" validate:"required"`
FirstName string `json:"firstName,omitempty"`
LastName string `json:"lastName,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Email string `json:"email,omitempty"`
Timezone string `json:"timezone,omitempty"`
Locale string `json:"locale,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// DBUserUpdate is the request body for updating a user record
type DBUserUpdate struct {
Timezone *string `json:"timezone,omitempty"`
Locale *string `json:"locale,omitempty"`
Status *string `json:"status,omitempty"`
LastLoginAt *time.Time `json:"lastLoginAt,omitempty"`
LastActivityAt *time.Time `json:"lastActivityAt,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// Certificate represents an EJBCA certificate
type Certificate struct {
SerialNumber string `json:"serialNumber"`
SubjectDN string `json:"subjectDn"`
IssuerDN string `json:"issuerDn"`
Status string `json:"status"`
NotBefore time.Time `json:"notBefore"`
NotAfter time.Time `json:"notAfter"`
KeyAlgorithm string `json:"keyAlgorithm,omitempty"`
CAName string `json:"caName,omitempty"`
CertProfileID int `json:"certProfileId,omitempty"`
}
// CertRequest is the request body for requesting a certificate
type CertRequest struct {
SubjectDN string `json:"subjectDn" validate:"required"`
CAName string `json:"caName" validate:"required"`
CertProfileName string `json:"certProfileName" validate:"required"`
EndEntityName string `json:"endEntityName" validate:"required"`
KeyAlgorithm string `json:"keyAlgorithm,omitempty"`
SANs []string `json:"sans,omitempty"`
}
// CertRenew is the request body for renewing a certificate
type CertRenew struct {
CSR string `json:"csr,omitempty"`
}
// CertRevoke is the request body for revoking a certificate
type CertRevoke struct {
Reason string `json:"reason"`
IssuerDN string `json:"issuerDn"`
}
// LDAPEntity represents a generic LDAP entity (tenant, policy, key, etc.)
type LDAPEntity struct {
DN string `json:"dn"`
Type string `json:"type"`
RDN string `json:"rdn"`
ObjectClasses []string `json:"objectClasses,omitempty"`
Attributes map[string]interface{} `json:"attributes"`
}
// LDAPEntityCreate is the request body for creating an entity
type LDAPEntityCreate struct {
Attributes map[string]interface{} `json:"attributes" validate:"required"`
}
// LDAPEntityUpdate is the request body for updating an entity
type LDAPEntityUpdate struct {
Attributes map[string]interface{} `json:"attributes" validate:"required"`
}
// PGPKey represents a Hockeypuck PGP key
type PGPKey struct {
KeyID string `json:"keyId"`
Fingerprint string `json:"fingerprint"`
Algorithm string `json:"algorithm,omitempty"`
Length int `json:"length,omitempty"`
Created string `json:"created,omitempty"`
Expires string `json:"expires,omitempty"`
UIDs []string `json:"uids,omitempty"`
ArmoredKey string `json:"armoredKey,omitempty"`
}
// PGPKeyUpload is the request body for uploading a PGP key
type PGPKeyUpload struct {
KeyText string `json:"keyText" validate:"required"`
}
// CardDAVPrincipal represents a sabre/dav principal
type CardDAVPrincipal struct {
ID int `json:"id"`
URI string `json:"uri"`
Email string `json:"email,omitempty"`
DisplayName string `json:"displayName,omitempty"`
}
// CardDAVPrincipalCreate is the request body for creating a principal
type CardDAVPrincipalCreate struct {
Username string `json:"username" validate:"required"`
Email string `json:"email,omitempty"`
DisplayName string `json:"displayName,omitempty"`
}
// AddressBook represents a sabre/dav address book
type AddressBook struct {
ID int `json:"id"`
PrincipalURI string `json:"principalUri"`
DisplayName string `json:"displayName"`
URI string `json:"uri"`
Description string `json:"description,omitempty"`
SyncToken int `json:"syncToken"`
}
// AddressBookCreate is the request body for creating an address book
type AddressBookCreate struct {
PrincipalURI string `json:"principalUri" validate:"required"`
DisplayName string `json:"displayName" validate:"required"`
URI string `json:"uri" validate:"required"`
Description string `json:"description,omitempty"`
}
// AddressBookUpdate is the request body for updating an address book
type AddressBookUpdate struct {
DisplayName *string `json:"displayName,omitempty"`
Description *string `json:"description,omitempty"`
}
// Contact represents a sabre/dav contact (card)
type Contact struct {
ID int `json:"id"`
AddressBookID int `json:"addressbookId"`
CardData string `json:"cardData,omitempty"`
URI string `json:"uri"`
LastModified int `json:"lastModified,omitempty"`
ETag string `json:"etag"`
Size int `json:"size"`
}
// ContactCreate is the request body for creating a contact
type ContactCreate struct {
URI string `json:"uri" validate:"required"`
CardData string `json:"cardData" validate:"required"`
}
// ContactUpdate is the request body for updating a contact
type ContactUpdate struct {
CardData string `json:"cardData" validate:"required"`
}
// ListParams represents common query parameters for list endpoints
type ListParams struct {
Limit int `query:"limit"`
Offset int `query:"offset"`
Search string `query:"search"`
Status string `query:"status"`
}
// DefaultListParams returns list params with defaults applied
func DefaultListParams(p ListParams) ListParams {
if p.Limit <= 0 || p.Limit > 100 {
p.Limit = 50
}
if p.Offset < 0 {
p.Offset = 0
}
return p
}

283
pkg/types/pbx.go Normal file
View File

@@ -0,0 +1,283 @@
package types
import (
"time"
"github.com/google/uuid"
)
// PBXTrunk represents a SIP trunk configuration
type PBXTrunk struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
ProviderID *uuid.UUID `json:"providerId,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
TrunkType string `json:"trunkType"`
Status string `json:"status"`
Host string `json:"host"`
Port int `json:"port"`
Transport string `json:"transport"`
Username string `json:"username,omitempty"`
AuthRealm string `json:"authRealm,omitempty"`
FromDomain string `json:"fromDomain,omitempty"`
FromUser string `json:"fromUser,omitempty"`
Register bool `json:"register"`
Codecs []string `json:"codecs"`
DtmfMode string `json:"dtmfMode"`
NatMode string `json:"natMode,omitempty"`
MaxChannels *int `json:"maxChannels,omitempty"`
CurrentChannels *int `json:"currentChannels,omitempty"`
OutboundCallerIDName string `json:"outboundCallerIdName,omitempty"`
OutboundCallerIDNum string `json:"outboundCallerIdNumber,omitempty"`
Priority *int `json:"priority,omitempty"`
DIDCount int `json:"didCount,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PBXTrunkCreate is the request body for creating a trunk
type PBXTrunkCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
ProviderID *uuid.UUID `json:"providerId,omitempty"`
Name string `json:"name" validate:"required"`
Description string `json:"description,omitempty"`
Host string `json:"host" validate:"required"`
Port int `json:"port,omitempty"`
Transport string `json:"transport,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
AuthRealm string `json:"authRealm,omitempty"`
FromDomain string `json:"fromDomain,omitempty"`
FromUser string `json:"fromUser,omitempty"`
Register bool `json:"register"`
Codecs []string `json:"codecs,omitempty"`
DtmfMode string `json:"dtmfMode,omitempty"`
NatMode string `json:"natMode,omitempty"`
MaxChannels *int `json:"maxChannels,omitempty"`
OutboundCallerIDName string `json:"outboundCallerIdName,omitempty"`
OutboundCallerIDNum string `json:"outboundCallerIdNumber,omitempty"`
}
// PBXTrunkUpdate is the request body for updating a trunk
type PBXTrunkUpdate struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Host *string `json:"host,omitempty"`
Port *int `json:"port,omitempty"`
Transport *string `json:"transport,omitempty"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
AuthRealm *string `json:"authRealm,omitempty"`
FromDomain *string `json:"fromDomain,omitempty"`
FromUser *string `json:"fromUser,omitempty"`
Register *bool `json:"register,omitempty"`
Codecs []string `json:"codecs,omitempty"`
DtmfMode *string `json:"dtmfMode,omitempty"`
NatMode *string `json:"natMode,omitempty"`
MaxChannels *int `json:"maxChannels,omitempty"`
OutboundCallerIDName *string `json:"outboundCallerIdName,omitempty"`
OutboundCallerIDNum *string `json:"outboundCallerIdNumber,omitempty"`
Status *string `json:"status,omitempty"`
}
// PBXTrunkDID represents a DID number assigned to a trunk
type PBXTrunkDID struct {
ID uuid.UUID `json:"id"`
TrunkID uuid.UUID `json:"trunkId"`
TenantID uuid.UUID `json:"tenantId"`
DIDNumber string `json:"didNumber"`
Description string `json:"description,omitempty"`
DestinationType string `json:"destinationType,omitempty"`
DestinationID *uuid.UUID `json:"destinationId,omitempty"`
IsActive bool `json:"isActive"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
}
// PBXTrunkDIDCreate is the request body for adding a DID to a trunk
type PBXTrunkDIDCreate struct {
DIDNumber string `json:"didNumber" validate:"required"`
TenantID uuid.UUID `json:"tenantId" validate:"required"`
Description string `json:"description,omitempty"`
DestinationType string `json:"destinationType,omitempty"`
DestinationID *uuid.UUID `json:"destinationId,omitempty"`
}
// PBXExtension represents a PBX extension
type PBXExtension struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
UserID *uuid.UUID `json:"userId,omitempty"`
Extension string `json:"extension"`
Name string `json:"name"`
ExtensionType string `json:"extensionType"`
CallerIDName string `json:"callerIdName,omitempty"`
CallerIDNumber string `json:"callerIdNumber,omitempty"`
OutboundCallerIDNumber string `json:"outboundCallerIdNumber,omitempty"`
SIPUsername string `json:"sipUsername,omitempty"`
Transport string `json:"transport"`
Codecs []string `json:"codecs"`
VoicemailEnabled bool `json:"voicemailEnabled"`
IsActive bool `json:"isActive"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PBXExtensionCreate is the request body for creating an extension
type PBXExtensionCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
UserID *uuid.UUID `json:"userId,omitempty"`
Extension string `json:"extension" validate:"required"`
Name string `json:"name" validate:"required"`
ExtensionType string `json:"extensionType,omitempty"`
CallerIDName string `json:"callerIdName,omitempty"`
CallerIDNumber string `json:"callerIdNumber,omitempty"`
OutboundCallerIDNumber string `json:"outboundCallerIdNumber,omitempty"`
SIPUsername string `json:"sipUsername,omitempty"`
SIPPassword string `json:"sipPassword,omitempty"`
Transport string `json:"transport,omitempty"`
Codecs []string `json:"codecs,omitempty"`
VoicemailEnabled bool `json:"voicemailEnabled"`
}
// PBXExtensionUpdate is the request body for updating an extension
type PBXExtensionUpdate struct {
Name *string `json:"name,omitempty"`
CallerIDName *string `json:"callerIdName,omitempty"`
CallerIDNumber *string `json:"callerIdNumber,omitempty"`
OutboundCallerIDNumber *string `json:"outboundCallerIdNumber,omitempty"`
SIPPassword *string `json:"sipPassword,omitempty"`
Codecs []string `json:"codecs,omitempty"`
VoicemailEnabled *bool `json:"voicemailEnabled,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
}
// PBXInboundRoute represents an inbound call route
type PBXInboundRoute struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
Name string `json:"name"`
DIDNumber string `json:"didNumber,omitempty"`
DestinationType string `json:"destinationType"`
DestinationID *uuid.UUID `json:"destinationId,omitempty"`
DestinationData string `json:"destinationData,omitempty"`
Priority int `json:"priority"`
IsActive bool `json:"isActive"`
TrunkID *uuid.UUID `json:"trunkId,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PBXInboundRouteCreate is the request body for creating an inbound route
type PBXInboundRouteCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
Name string `json:"name" validate:"required"`
DIDNumber string `json:"didNumber,omitempty"`
DestinationType string `json:"destinationType" validate:"required"`
DestinationID *uuid.UUID `json:"destinationId,omitempty"`
DestinationData string `json:"destinationData,omitempty"`
Priority int `json:"priority,omitempty"`
TrunkID *uuid.UUID `json:"trunkId,omitempty"`
}
// PBXInboundRouteUpdate is the request body for updating an inbound route
type PBXInboundRouteUpdate struct {
Name *string `json:"name,omitempty"`
DIDNumber *string `json:"didNumber,omitempty"`
DestinationType *string `json:"destinationType,omitempty"`
DestinationID *uuid.UUID `json:"destinationId,omitempty"`
DestinationData *string `json:"destinationData,omitempty"`
Priority *int `json:"priority,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
TrunkID *uuid.UUID `json:"trunkId,omitempty"`
}
// PBXOutboundRoute represents an outbound call route
type PBXOutboundRoute struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
Name string `json:"name"`
Priority int `json:"priority"`
DialPatterns []string `json:"dialPatterns"`
Prepend string `json:"prepend,omitempty"`
Prefix string `json:"prefix,omitempty"`
StripDigits int `json:"stripDigits"`
OverrideCallerID bool `json:"overrideCallerId"`
CallerIDName string `json:"callerIdName,omitempty"`
CallerIDNumber string `json:"callerIdNumber,omitempty"`
IsEmergency bool `json:"isEmergency"`
IsActive bool `json:"isActive"`
Trunks []PBXOutboundRouteTrunk `json:"trunks,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PBXOutboundRouteTrunk represents a trunk priority in an outbound route
type PBXOutboundRouteTrunk struct {
TrunkID uuid.UUID `json:"trunkId"`
TrunkName string `json:"trunkName,omitempty"`
Priority int `json:"priority"`
}
// PBXOutboundRouteCreate is the request body for creating an outbound route
type PBXOutboundRouteCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
Name string `json:"name" validate:"required"`
Priority int `json:"priority,omitempty"`
DialPatterns []string `json:"dialPatterns" validate:"required"`
Prepend string `json:"prepend,omitempty"`
Prefix string `json:"prefix,omitempty"`
StripDigits int `json:"stripDigits,omitempty"`
OverrideCallerID bool `json:"overrideCallerId"`
CallerIDName string `json:"callerIdName,omitempty"`
CallerIDNumber string `json:"callerIdNumber,omitempty"`
IsEmergency bool `json:"isEmergency"`
Trunks []PBXOutboundRouteTrunk `json:"trunks,omitempty"`
}
// PBXOutboundRouteUpdate is the request body for updating an outbound route
type PBXOutboundRouteUpdate struct {
Name *string `json:"name,omitempty"`
Priority *int `json:"priority,omitempty"`
DialPatterns []string `json:"dialPatterns,omitempty"`
Prepend *string `json:"prepend,omitempty"`
Prefix *string `json:"prefix,omitempty"`
StripDigits *int `json:"stripDigits,omitempty"`
OverrideCallerID *bool `json:"overrideCallerId,omitempty"`
CallerIDName *string `json:"callerIdName,omitempty"`
CallerIDNumber *string `json:"callerIdNumber,omitempty"`
IsEmergency *bool `json:"isEmergency,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
Trunks []PBXOutboundRouteTrunk `json:"trunks,omitempty"`
}
// PBXStatus represents the overall PBX system status
type PBXStatus struct {
AsteriskServers []PBXServerStatus `json:"asteriskServers"`
KamailioServers []PBXServerStatus `json:"kamailioServers"`
ActiveTrunks int `json:"activeTrunks"`
ActiveExtensions int `json:"activeExtensions"`
ActiveCalls int `json:"activeCalls"`
}
// PBXServerStatus represents a single PBX server status
type PBXServerStatus struct {
Host string `json:"host"`
Status string `json:"status"`
Uptime string `json:"uptime,omitempty"`
Version string `json:"version,omitempty"`
Channels int `json:"channels,omitempty"`
}
// PBXReloadResult represents the result of a PBX reload operation
type PBXReloadResult struct {
AsteriskResults []PBXReloadServer `json:"asteriskResults"`
KamailioResults []PBXReloadServer `json:"kamailioResults"`
}
// PBXReloadServer represents a reload result for a single server
type PBXReloadServer struct {
Host string `json:"host"`
Success bool `json:"success"`
Message string `json:"message,omitempty"`
}

181
pkg/types/persona.go Normal file
View File

@@ -0,0 +1,181 @@
package types
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// PersonaSummary is the list view of a persona
type PersonaSummary struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
Name string `json:"name"`
Archetype *string `json:"archetype,omitempty"`
Status string `json:"status"`
IsDefault bool `json:"isDefault"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PersonaConfig is the full persona configuration
type PersonaConfig struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
Name string `json:"name"`
Archetype *string `json:"archetype,omitempty"`
VoiceTone *string `json:"voiceTone,omitempty"`
MBTI *string `json:"mbti,omitempty"`
Openness *float64 `json:"openness,omitempty"`
Conscientiousness *float64 `json:"conscientiousness,omitempty"`
Extraversion *float64 `json:"extraversion,omitempty"`
Agreeableness *float64 `json:"agreeableness,omitempty"`
Neuroticism *float64 `json:"neuroticism,omitempty"`
PositiveRules json.RawMessage `json:"positiveRules,omitempty"`
NegativeRules json.RawMessage `json:"negativeRules,omitempty"`
Backstory *string `json:"backstory,omitempty"`
WorldBuilding *string `json:"worldBuilding,omitempty"`
GuardrailsConfig json.RawMessage `json:"guardrailsConfig,omitempty"`
TopicalRails *string `json:"topicalRails,omitempty"`
Status string `json:"status"`
DefaultModel *string `json:"defaultModel,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
MaxTokensPerTurn *int `json:"maxTokensPerTurn,omitempty"`
MoralCare *float64 `json:"moralCare,omitempty"`
MoralFairness *float64 `json:"moralFairness,omitempty"`
MoralRights *float64 `json:"moralRights,omitempty"`
MoralLoyalty *float64 `json:"moralLoyalty,omitempty"`
MoralAuthority *float64 `json:"moralAuthority,omitempty"`
MoralSanctity *float64 `json:"moralSanctity,omitempty"`
IsDefault bool `json:"isDefault"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// PersonaCreate is the request body for creating a persona
type PersonaCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
Name string `json:"name" validate:"required"`
Archetype *string `json:"archetype,omitempty"`
VoiceTone *string `json:"voiceTone,omitempty"`
MBTI *string `json:"mbti,omitempty"`
Openness *float64 `json:"openness,omitempty"`
Conscientiousness *float64 `json:"conscientiousness,omitempty"`
Extraversion *float64 `json:"extraversion,omitempty"`
Agreeableness *float64 `json:"agreeableness,omitempty"`
Neuroticism *float64 `json:"neuroticism,omitempty"`
PositiveRules json.RawMessage `json:"positiveRules,omitempty"`
NegativeRules json.RawMessage `json:"negativeRules,omitempty"`
Backstory *string `json:"backstory,omitempty"`
WorldBuilding *string `json:"worldBuilding,omitempty"`
GuardrailsConfig json.RawMessage `json:"guardrailsConfig,omitempty"`
TopicalRails *string `json:"topicalRails,omitempty"`
DefaultModel *string `json:"defaultModel,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
MaxTokensPerTurn *int `json:"maxTokensPerTurn,omitempty"`
MoralCare *float64 `json:"moralCare,omitempty"`
MoralFairness *float64 `json:"moralFairness,omitempty"`
MoralRights *float64 `json:"moralRights,omitempty"`
MoralLoyalty *float64 `json:"moralLoyalty,omitempty"`
MoralAuthority *float64 `json:"moralAuthority,omitempty"`
MoralSanctity *float64 `json:"moralSanctity,omitempty"`
}
// PersonaUpdate is the request body for updating a persona
type PersonaUpdate struct {
Name *string `json:"name,omitempty"`
Archetype *string `json:"archetype,omitempty"`
VoiceTone *string `json:"voiceTone,omitempty"`
MBTI *string `json:"mbti,omitempty"`
Openness *float64 `json:"openness,omitempty"`
Conscientiousness *float64 `json:"conscientiousness,omitempty"`
Extraversion *float64 `json:"extraversion,omitempty"`
Agreeableness *float64 `json:"agreeableness,omitempty"`
Neuroticism *float64 `json:"neuroticism,omitempty"`
PositiveRules json.RawMessage `json:"positiveRules,omitempty"`
NegativeRules json.RawMessage `json:"negativeRules,omitempty"`
Backstory *string `json:"backstory,omitempty"`
WorldBuilding *string `json:"worldBuilding,omitempty"`
GuardrailsConfig json.RawMessage `json:"guardrailsConfig,omitempty"`
TopicalRails *string `json:"topicalRails,omitempty"`
Status *string `json:"status,omitempty"`
DefaultModel *string `json:"defaultModel,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
MaxTokensPerTurn *int `json:"maxTokensPerTurn,omitempty"`
MoralCare *float64 `json:"moralCare,omitempty"`
MoralFairness *float64 `json:"moralFairness,omitempty"`
MoralRights *float64 `json:"moralRights,omitempty"`
MoralLoyalty *float64 `json:"moralLoyalty,omitempty"`
MoralAuthority *float64 `json:"moralAuthority,omitempty"`
MoralSanctity *float64 `json:"moralSanctity,omitempty"`
IsDefault *bool `json:"isDefault,omitempty"`
}
// SelfModelSnapshot contains identity constraints, commitments, and conscience standards
type SelfModelSnapshot struct {
IdentityConstraints []IdentityConstraint `json:"identityConstraints"`
Commitments []PersonaCommitment `json:"commitments"`
ConscienceStandards []ConscienceStandard `json:"conscienceStandards"`
}
// IdentityConstraint defines a persona's behavioral boundary
type IdentityConstraint struct {
ConstraintType string `json:"type"`
ConstraintText string `json:"text"`
Description *string `json:"description,omitempty"`
Source *string `json:"source,omitempty"`
Strength float64 `json:"strength"`
}
// PersonaCommitment defines a persona's commitment
type PersonaCommitment struct {
CommitmentText string `json:"text"`
CommitmentType *string `json:"type,omitempty"`
Source *string `json:"source,omitempty"`
Strength float64 `json:"strength"`
}
// ConscienceStandard defines a moral standard for a persona
type ConscienceStandard struct {
StandardText string `json:"text"`
StandardType *string `json:"type,omitempty"`
MoralFoundation *string `json:"moralFoundation,omitempty"`
Strength float64 `json:"strength"`
}
// Experience represents an episodic experience
type Experience struct {
ID uuid.UUID `json:"id"`
EventSummary *string `json:"eventSummary,omitempty"`
EventType *string `json:"eventType,omitempty"`
OccurredAt *time.Time `json:"occurredAt,omitempty"`
Place *string `json:"place,omitempty"`
Actors *string `json:"actors,omitempty"`
Outcome *string `json:"outcome,omitempty"`
OutcomeDetail *string `json:"outcomeDetail,omitempty"`
EmotionalValence *float64 `json:"emotionalValence,omitempty"`
LessonLearned *string `json:"lessonLearned,omitempty"`
ImportanceScore *float64 `json:"importanceScore,omitempty"`
}
// MoralAssessment represents a moral assessment for a session
type MoralAssessment struct {
ActivatedFoundations json.RawMessage `json:"activatedFoundations,omitempty"`
AssessmentText *string `json:"assessmentText,omitempty"`
HasTension *bool `json:"hasTension,omitempty"`
TensionFoundations *string `json:"tensionFoundations,omitempty"`
ResolutionFoundation *string `json:"resolutionFoundation,omitempty"`
Confidence *float64 `json:"confidence,omitempty"`
}
// Evaluation represents a message evaluation
type Evaluation struct {
RoleFidelity *string `json:"roleFidelity,omitempty"`
VoiceConsistency *string `json:"voiceConsistency,omitempty"`
SafetyCompliance *string `json:"safetyCompliance,omitempty"`
CharacterBreak *bool `json:"characterBreak,omitempty"`
DriftScore *float64 `json:"driftScore,omitempty"`
EvaluatorModel *string `json:"evaluatorModel,omitempty"`
EvaluatedAt *time.Time `json:"evaluatedAt,omitempty"`
}

View File

@@ -0,0 +1,25 @@
package types
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// UserAgentConfig represents a user's personal AI agent configuration
type UserAgentConfig struct {
ID uuid.UUID `json:"id"`
UserID uuid.UUID `json:"userId"`
TenantID uuid.UUID `json:"tenantId"`
Config json.RawMessage `json:"config"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// UserAgentConfigUpsert is the request body for creating/updating a user agent config
type UserAgentConfigUpsert struct {
UserID uuid.UUID `json:"userId" validate:"required"`
TenantID uuid.UUID `json:"tenantId" validate:"required"`
Config json.RawMessage `json:"config" validate:"required"`
}

46
pkg/types/response.go Normal file
View File

@@ -0,0 +1,46 @@
package types
// Response is the standard API response envelope
type Response struct {
Data interface{} `json:"data,omitempty"`
Meta *Meta `json:"meta,omitempty"`
Error *APIError `json:"error,omitempty"`
RequestID string `json:"requestId,omitempty"`
}
// Meta contains pagination and count metadata
type Meta struct {
Total int64 `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
// NewDataResponse creates a successful response with data
func NewDataResponse(data interface{}, requestID string) *Response {
return &Response{
Data: data,
RequestID: requestID,
}
}
// NewPagedResponse creates a successful response with data and pagination
func NewPagedResponse(data interface{}, total int64, limit, offset int, requestID string) *Response {
return &Response{
Data: data,
Meta: &Meta{
Total: total,
Limit: limit,
Offset: offset,
},
RequestID: requestID,
}
}
// NewErrorResponse creates an error response
func NewErrorResponse(err *APIError, requestID string) *Response {
err.RequestID = requestID
return &Response{
Error: err,
RequestID: requestID,
}
}

115
pkg/types/voice_agent.go Normal file
View File

@@ -0,0 +1,115 @@
package types
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// VoiceAgentConfig represents a voice agent configuration
type VoiceAgentConfig struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
AgentID uuid.UUID `json:"agentId"`
GreetingText string `json:"greetingText"`
GoodbyeText string `json:"goodbyeText"`
VoiceID string `json:"voiceId"`
Language string `json:"language"`
STTProvider *string `json:"sttProvider,omitempty"`
STTModel *string `json:"sttModel,omitempty"`
TTSProvider *string `json:"ttsProvider,omitempty"`
TTSModel *string `json:"ttsModel,omitempty"`
MaxCallDurationSeconds int `json:"maxCallDurationSeconds"`
SilenceTimeoutSeconds int `json:"silenceTimeoutSeconds"`
BargeInEnabled bool `json:"bargeInEnabled"`
VADSensitivity string `json:"vadSensitivity"`
TransferEnabled bool `json:"transferEnabled"`
TransferNumber *string `json:"transferNumber,omitempty"`
BusinessHoursEnabled bool `json:"businessHoursEnabled"`
BusinessHours json.RawMessage `json:"businessHours,omitempty"`
AfterHoursText *string `json:"afterHoursText,omitempty"`
IsActive bool `json:"isActive"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
}
// VoiceAgentConfigCreate is the request body for creating a voice agent config
type VoiceAgentConfigCreate struct {
TenantID uuid.UUID `json:"tenantId" validate:"required"`
AgentID uuid.UUID `json:"agentId" validate:"required"`
GreetingText string `json:"greetingText,omitempty"`
GoodbyeText string `json:"goodbyeText,omitempty"`
VoiceID string `json:"voiceId,omitempty"`
Language string `json:"language,omitempty"`
STTProvider *string `json:"sttProvider,omitempty"`
STTModel *string `json:"sttModel,omitempty"`
TTSProvider *string `json:"ttsProvider,omitempty"`
TTSModel *string `json:"ttsModel,omitempty"`
MaxCallDurationSeconds *int `json:"maxCallDurationSeconds,omitempty"`
SilenceTimeoutSeconds *int `json:"silenceTimeoutSeconds,omitempty"`
BargeInEnabled *bool `json:"bargeInEnabled,omitempty"`
VADSensitivity string `json:"vadSensitivity,omitempty"`
TransferEnabled *bool `json:"transferEnabled,omitempty"`
TransferNumber *string `json:"transferNumber,omitempty"`
BusinessHoursEnabled *bool `json:"businessHoursEnabled,omitempty"`
BusinessHours json.RawMessage `json:"businessHours,omitempty"`
AfterHoursText *string `json:"afterHoursText,omitempty"`
}
// VoiceAgentConfigUpdate is the request body for updating a voice agent config
type VoiceAgentConfigUpdate struct {
GreetingText *string `json:"greetingText,omitempty"`
GoodbyeText *string `json:"goodbyeText,omitempty"`
VoiceID *string `json:"voiceId,omitempty"`
Language *string `json:"language,omitempty"`
STTProvider *string `json:"sttProvider,omitempty"`
STTModel *string `json:"sttModel,omitempty"`
TTSProvider *string `json:"ttsProvider,omitempty"`
TTSModel *string `json:"ttsModel,omitempty"`
MaxCallDurationSeconds *int `json:"maxCallDurationSeconds,omitempty"`
SilenceTimeoutSeconds *int `json:"silenceTimeoutSeconds,omitempty"`
BargeInEnabled *bool `json:"bargeInEnabled,omitempty"`
VADSensitivity *string `json:"vadSensitivity,omitempty"`
TransferEnabled *bool `json:"transferEnabled,omitempty"`
TransferNumber *string `json:"transferNumber,omitempty"`
BusinessHoursEnabled *bool `json:"businessHoursEnabled,omitempty"`
BusinessHours json.RawMessage `json:"businessHours,omitempty"`
AfterHoursText *string `json:"afterHoursText,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
}
// VoiceSession represents a voice call session record
type VoiceSession struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
AgentID uuid.UUID `json:"agentId"`
CallerNumber *string `json:"callerNumber,omitempty"`
CalledNumber *string `json:"calledNumber,omitempty"`
AsteriskCallID *uuid.UUID `json:"asteriskCallId,omitempty"`
AgentSessionID *string `json:"agentSessionId,omitempty"`
TotalTurns int `json:"totalTurns"`
STTProvider *string `json:"sttProvider,omitempty"`
TTSProvider *string `json:"ttsProvider,omitempty"`
STTAudioSeconds *float64 `json:"sttAudioSeconds,omitempty"`
TTSCharacters *int `json:"ttsCharacters,omitempty"`
StartedAt *time.Time `json:"startedAt,omitempty"`
EndedAt *time.Time `json:"endedAt,omitempty"`
EndReason *string `json:"endReason,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
Turns []VoiceSessionTurn `json:"turns,omitempty"`
}
// VoiceSessionTurn represents a single turn in a voice session
type VoiceSessionTurn struct {
ID uuid.UUID `json:"id"`
SessionID uuid.UUID `json:"sessionId"`
TurnNumber int `json:"turnNumber"`
Role string `json:"role"`
Text string `json:"text"`
STTConfidence *float64 `json:"sttConfidence,omitempty"`
AgentLatencyMs *int `json:"agentLatencyMs,omitempty"`
WasInterrupted bool `json:"wasInterrupted"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
}