
    $j&                       d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
 ddlmZmZ ddlmZmZ ddlmZ d	d
lmZ  ej        e          Ze G d d                      Zd!dZd"dZdZ G d d          Ze
	 	 	 d#d$d             ZdS )%zAudit logging for feature usage tracking.

Provides fire-and-forget audit logging of all mutating and core operations
(retain, recall, reflect, bank CRUD, etc.) across HTTP, MCP, and system transports.
    )annotationsN)Callable)asynccontextmanager)	dataclassfield)datetimetimezone)Any   )acquire_with_retryc                      e Zd ZU dZded<   ded<   dZded<    ed 	          Zd
ed<   dZded<   dZ	ded<   dZ
ded<    ee	          Zded<   dS )
AuditEntryzA single audit log entry.straction	transportN
str | Nonebank_idc                 >    t          j        t          j                  S N)r   nowr	   utc     b/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/audit.py<lambda>zAuditEntry.<lambda>   s    hl9S9S r   )default_factoryr   
started_atzdatetime | Noneended_atdict[str, Any] | Nonerequestresponsezdict[str, Any]metadata)__name__
__module____qualname____doc____annotations__r   r   r   r   r    r!   dictr"   r   r   r   r   r      s         ##KKKNNNG 51S1STTTJTTTT $H$$$$%)G))))&*H****$uT:::H::::::r   r   objr
   returnr   c                :   t          | t                    r|                                 S t          | t          j                  rt          |           S t          | t                    rdS t          | t                    rt          |           S t          |           S )z8JSON serializer for objects not serializable by default.z<bytes>)	
isinstancer   	isoformatuuidUUIDr   bytessetlist)r)   s    r   _json_defaultr3   &   s    #x   }}#ty!! 3xx#u y#s Cyys88Or   datar   c                    | dS 	 t          j        | t                    S # t          $ r  t                              dd           Y dS w xY w)z9Serialize data to JSON string, returning None on failure.N)defaultzFailed to serialize audit dataT)exc_info)jsondumpsr3   	Exceptionloggerdebug)r4   s    r   
_safe_jsonr=   3   s_    |tz$6666   5EEEtts   ! &A
Ai  c                  V    e Zd ZdZ	 dddZddZddZddZd dZd dZ	d dZ
d dZdS )!AuditLoggerz?Fire-and-forget audit log writer with optional retention sweep.pool_getterCallable[[], Any]schema_getterCallable[[], str]enabledboolallowed_actions	list[str]retention_daysintr*   Nonec                |    || _         || _        || _        |rt          |          nd | _        || _        d | _        d S r   )_pool_getter_schema_getter_enabled	frozenset_allowed_actions_retention_days_sweep_task)selfrA   rC   rE   rG   rI   s         r   __init__zAuditLogger.__init__D   sL     (+Ud7ny7Q7Q7Qjn-04r   r   r   c                8    | j         sdS | j        	|| j        v S dS )z2Check if audit logging is enabled for this action.FNT)rO   rQ   )rT   r   s     r   
is_enabledzAuditLogger.is_enabledS   s-    } 	5 ,T222tr   entryr   c                    |                      |j                  sdS 	 t          j        |                     |                     dS # t
          $ r t                              d           Y dS w xY w)z-Schedule an audit write as a background task.Nz6Cannot schedule audit log write: no running event loop)rW   r   asynciocreate_task	_safe_logRuntimeErrorr;   r<   )rT   rX   s     r   log_fire_and_forgetzAuditLogger.log_fire_and_forget[   s    u|,, 	F	Su 5 566666 	S 	S 	SLLQRRRRRR	Ss   'A $A/.A/c                  K   |                                  }|t                              d           dS 	 |                                 }| d}t	          |d          4 d{V }|                    d| dt          j                    |j        |j	        |j
        |j        |j        t          |j                  t          |j                  t          |j                  pd
  
         d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS # t"          $ r0}t                              d	|j         d
|            Y d}~dS d}~ww xY w)z9Write audit entry to DB. Errors are logged, never raised.Nz%Audit log skipped: pool not available
.audit_log   max_retriesz!
                    INSERT INTO z
                        (id, action, transport, bank_id, started_at, ended_at, request, response, metadata)
                    VALUES
                        ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, $9::jsonb)
                    z{}z"Audit log write failed for action=z: )rM   r;   r<   rN   r   executer.   uuid4r   r   r   r   r   r=   r    r!   r"   r:   warning)rT   rX   poolschematableconnes          r   r\   zAuditLogger._safe_loge   s        ""<LL@AAAF	U((**F)))E)$A>>>       $ll!&   JLLLOM$Nu}--u~..u~..6$                                     $  	U 	U 	UNNSSSPQSSTTTTTTTTT	Us<   0D &BD1D 
DD DD 
E!%EEc                    | j         dk    s| j        sdS 	 t          j        |                                           | _        dS # t          $ r t                              d           Y dS w xY w)z>Start the periodic retention sweep if retention is configured.r   Nz3Cannot start retention sweep: no running event loop)	rR   rO   rZ   r[   _sweep_looprS   r]   r;   r<   rT   s    r   start_retention_sweepz!AuditLogger.start_retention_sweep   s    1$$DM$F	P&243C3C3E3EFFD 	P 	P 	PLLNOOOOOO	Ps   +A $A+*A+c                   K   | j         r_| j                                         sH| j                                          	 | j          d{V  n# t          j        $ r Y nw xY wd| _         dS dS dS )z"Stop the periodic retention sweep.N)rS   donecancelrZ   CancelledErrorrn   s    r   stop_retention_sweepz AuditLogger.stop_retention_sweep   s       	$D$4$9$9$;$; 	$##%%%&&&&&&&&&)   #D	$ 	$ 	$ 	$s   A AAc                |   K   	 |                                   d{V  t          j        t                     d{V  :)z@Periodically delete audit log entries older than retention_days.TN)
_run_sweeprZ   sleep_SWEEP_INTERVAL_SECONDSrn   s    r   rm   zAuditLogger._sweep_loop   sU      	9//#########- 7888888888	9r   c                  K   |                                  }|dS 	 |                                 }| d}t          |d          4 d{V }|                    d| d| j         d           d{V }|r#|dk    rt
                              d	|            ddd          d{V  dS # 1 d{V swxY w Y   dS # t          $ r(}t
                              d
|            Y d}~dS d}~ww xY w)zHDelete expired audit log entries. Concurrent-safe via row-level deletes.Nr`   ra   rb   zDELETE FROM z& WHERE started_at < NOW() - INTERVAL 'z days'zDELETE 0zAudit log retention sweep: z"Audit log retention sweep failed: )	rM   rN   r   rd   rR   r;   infor:   rf   )rT   rg   rh   ri   rj   resultrk   s          r   rv   zAuditLogger._run_sweep   s       ""<F
	E((**F)))E)$A>>> H H H H H H H$#||l5llPTPdlll           Hf
22KK Ff F FGGGH H H H H H H H H H H H H H H H H H H H H H H H H H H H H H  	E 	E 	ENNCCCDDDDDDDDD	Es<   0B? AB,B? ,
B66B? 9B6:B? ?
C1	C,,C1N)r@   )rA   rB   rC   rD   rE   rF   rG   rH   rI   rJ   r*   rK   )r   r   r*   rF   )rX   r   r*   rK   )r*   rK   )r#   r$   r%   r&   rU   rW   r^   r\   ro   rt   rm   rv   r   r   r   r?   r?   A   s        II !5 5 5 5 5   S S S SU U U U<P P P P$ $ $ $9 9 9 9E E E E E Er   r?   audit_loggerAuditLogger | Noner   r   r   r    r   r"   c                 K   | |                      |          st          |||          }|W V  dS t          |||t          j        t          j                  ||pi           }	 |W V  t          j        t          j                  |_        |                     |           dS # t          j        t          j                  |_        |                     |           w xY w)a  Async context manager that times the operation and writes audit on exit.

    Usage:
        async with audit_context(logger, "retain", "http", bank_id, request_dict) as entry:
            result = await do_work()
            entry.response = result_dict
    N)r   r   r   )r   r   r   r   r    r"   )rW   r   r   r   r	   r   r   r^   )r|   r   r   r   r    r"   rX   s          r   audit_contextr      s        <#:#:6#B#B&IwOOO<--R  E0!hl33((///// "hl33((////s   'B& &:C )r)   r
   r*   r   )r4   r
   r*   r   )NNN)r|   r}   r   r   r   r   r   r   r    r   r"   r   )r&   
__future__r   rZ   r8   loggingr.   collections.abcr   
contextlibr   dataclassesr   r   r   r	   typingr
   engine.db_utilsr   	getLoggerr#   r;   r   r3   r=   rx   r?   r   r   r   r   <module>r      s    # " " " " "     $ $ $ $ $ $ * * * * * * ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' '       0 0 0 0 0 0		8	$	$ 
; 
; 
; 
; 
; 
; 
; 
;
 
 
 
     jE jE jE jE jE jE jE jEZ 
 %)&* 0  0  0  0  0  0  0r   