EntraTripwire.psm1

#Requires -Version 7.2

<#
    EntraTripwire -- deception-based intrusion detection for Microsoft Entra ID.

    Loader. Files are dot-sourced private-first, then the public surface. Only the
    functions in Public/ are exported; everything else stays internal so the module's
    contract is the documented command set rather than whatever happens to be defined.
#>


# Version 1.0 on purpose, not Latest. Microsoft Graph omits optional properties instead
# of returning null -- signInActivity on an unlicensed tenant, location on a sign-in from
# a private range, @odata.nextLink on a final page. StrictMode 2.0+ turns "Graph did not
# return this field" into a terminating error, which is exactly the wrong failure for a
# detector: it converts missing evidence into a crash instead of a withheld signal.
Set-StrictMode -Version 1.0

foreach ($folder in @('Private', 'Public')) {
    $path = Join-Path -Path $PSScriptRoot -ChildPath $folder
    if (-not (Test-Path -LiteralPath $path)) { continue }

    foreach ($file in (Get-ChildItem -Path $path -Filter '*.ps1' -File | Sort-Object Name)) {
        try {
            . $file.FullName
        }
        catch {
            throw "Failed to load '$($file.FullName)': $($_.Exception.Message)"
        }
    }
}

# Declared before anything can read it. StrictMode turns a read of an unset variable into
# a terminating error, so a module-scope variable that is only ever assigned by
# Connect-EntraTripwire would make every command that consults the session crash in a
# session that has not connected -- which is exactly the DEMO-mode path this module
# promises works with no tenant.
$script:TripwireSession = $null

$publicPath = Join-Path -Path $PSScriptRoot -ChildPath 'Public'
$publicFunctions = @(
    Get-ChildItem -Path $publicPath -Filter '*.ps1' -File -ErrorAction SilentlyContinue |
        ForEach-Object { $_.BaseName }
)

Export-ModuleMember -Function $publicFunctions