Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e06c076
Initial plan for issue
Copilot Jun 1, 2025
6c8a680
Implement disk-based context operations for cross-process sharing
Copilot Jun 1, 2025
71fc57e
Update argument completers to use disk-based context enumeration
Copilot Jun 1, 2025
310c6a8
Update argument completer to use Get-ContextInfo for better consistency
Copilot Jun 1, 2025
f9dc277
Refactor argument completer to use dynamic command names and improve …
MariusStorhaug Jun 1, 2025
358c623
Remove Contexts.ps1 file and its concurrent dictionary implementation…
MariusStorhaug Jun 1, 2025
b17494e
Downgrade Sodium module version to 2.1.2 in context functions for com…
MariusStorhaug Jun 1, 2025
3942461
Update Sodium module version to 2.2.0 in context functions for compat…
MariusStorhaug Jun 1, 2025
a033a2f
Add blank line at the beginning of README.md for improved readability
MariusStorhaug Jun 2, 2025
a9e03a2
Remove unnecessary blank line at the beginning of README.md for impro…
MariusStorhaug Jun 2, 2025
e8d2a6a
Add blank line at the beginning of README.md for improved readability
MariusStorhaug Jun 2, 2025
d94239e
Remove unnecessary blank line at the beginning of README.md for impro…
MariusStorhaug Jun 2, 2025
4237d12
Add blank line at the beginning of README.md for improved readability
MariusStorhaug Jun 2, 2025
040e709
Remove unnecessary blank line at the beginning of README.md for impro…
MariusStorhaug Jun 2, 2025
a9a8b6f
Add blank line at the beginning of README.md for improved readability
MariusStorhaug Jun 2, 2025
0515f23
Remove unnecessary blank line at the beginning of README.md for impro…
MariusStorhaug Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/completers.ps1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Register-ArgumentCompleter -CommandName 'Get-Context', 'Set-Context', 'Remove-Context', 'Rename-Context' -ParameterName 'ID' -ScriptBlock {
Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) -ParameterName 'ID' -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
$null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter

$script:Contexts.Values.ID | Where-Object { $_ -like "$wordToComplete*" } |
ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
$contextInfos = Get-ContextInfo -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false
$contextInfos | Where-Object { $_.ID -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_.ID, $_.ID, 'ParameterValue', $_.ID)
}
}
76 changes: 0 additions & 76 deletions src/functions/private/Import-Context.ps1

This file was deleted.

9 changes: 4 additions & 5 deletions src/functions/private/Set-ContextVault.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Requires -Modules @{ ModuleName = 'Sodium'; RequiredVersion = '2.1.2' }
#Requires -Modules @{ ModuleName = 'Sodium'; RequiredVersion = '2.2.0' }

function Set-ContextVault {
<#
Expand All @@ -7,7 +7,7 @@ function Set-ContextVault {

.DESCRIPTION
Sets the context vault. If the vault does not exist, it will be initialized.
Once the context vault is set, it will be imported into memory.
Once the context vault is set, the keys will be prepared for use.
The vault consists of multiple security shards, including a machine-specific shard,
a user-specific shard, and a seed shard stored within the vault directory.

Expand All @@ -20,7 +20,7 @@ function Set-ContextVault {
None.

.NOTES
This function modifies the script-scoped configuration and imports the vault.
This function modifies the script-scoped configuration and prepares the vault for use.

.LINK
https://psmodule.io/Context/Functions/Set-ContextVault
Expand Down Expand Up @@ -59,7 +59,7 @@ function Set-ContextVault {
$machineShard = [System.Environment]::MachineName
$userShard = [System.Environment]::UserName
#$userInputShard = Read-Host -Prompt 'Enter a seed shard' # Eventually 4 shards. +1 for user input.
$seed = $machineShard + $userShard + $seedShard + $userInputShard
$seed = $machineShard + $userShard + $seedShard # + $userInputShard
$keys = New-SodiumKeyPair -Seed $seed
$script:Config.PrivateKey = $keys.PrivateKey
$script:Config.PublicKey = $keys.PublicKey
Expand All @@ -73,6 +73,5 @@ function Set-ContextVault {

end {
Write-Debug "[$stackPath] - End"
Import-Context
}
}
32 changes: 26 additions & 6 deletions src/functions/public/Get-Context.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
function Get-Context {
#Requires -Modules @{ ModuleName = 'Sodium'; RequiredVersion = '2.2.0' }

function Get-Context {
<#
.SYNOPSIS
Retrieves a context from the in-memory context vault.
Retrieves a context from the context vault.

.DESCRIPTION
Retrieves a context from the loaded contexts stored in memory.
Retrieves a context by reading and decrypting context files directly from the vault directory.
If no ID is specified, all available contexts will be returned.
Wildcards are supported to match multiple contexts.

Expand Down Expand Up @@ -42,7 +44,7 @@
LoginTime : 2/9/2025 10:45:11 AM
```

Retrieves all contexts from the context vault (in memory).
Retrieves all contexts from the context vault (directly from disk).

.EXAMPLE
Get-Context -ID 'MySecret'
Expand All @@ -68,7 +70,7 @@
YourData : {DataKey=DataValue}
```

Retrieves all contexts that start with 'My' from the context vault (in memory).
Retrieves all contexts that start with 'My' from the context vault (directly from disk).

.OUTPUTS
[System.Object]
Expand Down Expand Up @@ -106,7 +108,25 @@
Write-Verbose "Retrieving contexts - ID: [$($ID -join ', ')]"
foreach ($item in $ID) {
Write-Verbose "Retrieving contexts - ID: [$item]"
$script:Contexts.Values | Where-Object { $_.ID -like $item } | Select-Object -ExpandProperty Context
# Read context files directly from disk instead of using in-memory cache
$contextFiles = Get-ChildItem -Path $script:Config.VaultPath -Filter *.json -File -Recurse
foreach ($file in $contextFiles) {
Comment thread
MariusStorhaug marked this conversation as resolved.
try {
$contextInfo = Get-Content -Path $file.FullName | ConvertFrom-Json
if ($contextInfo.ID -like $item) {
# Decrypt and return the context
$params = @{
SealedBox = $contextInfo.Context
PublicKey = $script:Config.PublicKey
PrivateKey = $script:Config.PrivateKey
}
$contextObj = ConvertFrom-SodiumSealedBox @params
ConvertFrom-ContextJson -JsonString $contextObj
}
} catch {
Write-Warning "Failed to read or decrypt context file: $($file.FullName). Error: $_"
}
}
}
}

Expand Down
28 changes: 22 additions & 6 deletions src/functions/public/Get-ContextInfo.ps1
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
function Get-ContextInfo {
<#
.SYNOPSIS
Retrieves info about a context from the in-memory context vault.
Retrieves info about a context from the context vault.

.DESCRIPTION
Retrieves info about context from the loaded contexts stored in memory.
Retrieves info about context files directly from the vault directory on disk.
If no ID is specified, all available info on contexts will be returned.
Wildcards are supported to match multiple contexts.
Only metadata (ID and Path) is returned without decrypting the context contents.

.EXAMPLE
Get-ContextInfo
Expand All @@ -29,7 +30,7 @@
Path : ...\feacc853-5bea-48d1-b751-41ce9768d48e.json
```

Retrieves all contexts from the context vault (in memory).
Retrieves all contexts from the context vault (directly from disk).

.EXAMPLE
Get-ContextInfo -ID 'MySecret'
Expand All @@ -40,7 +41,7 @@
Path : ...\3e223259-f242-4e97-91c8-f0fd054cfea7.json
```

Retrieves the context called 'MySecret' from the context vault (in memory).
Retrieves the context called 'MySecret' from the context vault (directly from disk).

.EXAMPLE
'My*' | Get-ContextInfo
Expand All @@ -57,7 +58,7 @@
Path : .../b7c01dbe-bccd-4c7e-b075-c5aac1c43b1a.json
```

Retrieves all contexts that start with 'My' from the context vault (in memory).
Retrieves all contexts that start with 'My' from the context vault (directly from disk).

.OUTPUTS
[System.Object]
Expand Down Expand Up @@ -94,7 +95,22 @@
process {
Write-Verbose "Retrieving context info - ID: [$ID]"
foreach ($item in $ID) {
$script:Contexts.Values | Where-Object { $_.ID -like $item } | Select-Object -ExcludeProperty Context
# Read context files directly from disk instead of using in-memory cache
$contextFiles = Get-ChildItem -Path $script:Config.VaultPath -Filter *.json -File -Recurse
foreach ($file in $contextFiles) {
try {
$contextInfo = Get-Content -Path $file.FullName | ConvertFrom-Json
if ($contextInfo.ID -like $item) {
# Return only metadata (ID and Path), don't decrypt the Context property
[PSCustomObject]@{
ID = $contextInfo.ID
Path = $contextInfo.Path
}
}
} catch {
Write-Warning "Failed to read context file: $($file.FullName). Error: $_"
}
}
}
}

Expand Down
24 changes: 13 additions & 11 deletions src/functions/public/Remove-Context.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,20 @@
process {
foreach ($item in $ID) {
Write-Verbose "Processing ID [$item]"
$script:Contexts.Keys | Where-Object { $_ -like $item } | ForEach-Object {
Write-Verbose "Removing context [$_]"
if ($PSCmdlet.ShouldProcess($_, 'Remove secret')) {
$script:Contexts[$_].Path | Remove-Item -Force

Write-Verbose "Attempting to remove context: $_"
[PSCustomObject]$removedItem = $null
if ($script:Contexts.TryRemove($_, [ref]$removedItem)) {
Write-Verbose "Removed item: $removedItem"
} else {
Write-Verbose 'Key not found'
# Find contexts by scanning disk files instead of using in-memory cache
$contextFiles = Get-ChildItem -Path $script:Config.VaultPath -Filter *.json -File -Recurse
foreach ($file in $contextFiles) {
try {
$contextInfo = Get-Content -Path $file.FullName | ConvertFrom-Json
if ($contextInfo.ID -like $item) {
Write-Verbose "Removing context [$($contextInfo.ID)]"
if ($PSCmdlet.ShouldProcess($contextInfo.ID, 'Remove secret')) {
$file.FullName | Remove-Item -Force
Write-Verbose "Removed context file: $($file.FullName)"
}
}
} catch {
Write-Warning "Failed to read context file: $($file.FullName). Error: $_"
}
}
}
Expand Down
42 changes: 21 additions & 21 deletions src/functions/public/Set-Context.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Requires -Modules @{ ModuleName = 'Sodium'; RequiredVersion = '2.1.2' }
#Requires -Modules @{ ModuleName = 'Sodium'; RequiredVersion = '2.2.0' }

function Set-Context {
<#
Expand All @@ -7,8 +7,8 @@ function Set-Context {

.DESCRIPTION
If the context does not exist, it will be created. If it already exists, it will be updated.
The context is cached in memory for faster access. This function ensures that the context
is securely stored using encryption mechanisms.
The context is securely stored on disk using encryption mechanisms.
Each context operation reads the current state from disk to ensure consistency across processes.

.EXAMPLE
Set-Context -ID 'PSModule.GitHub' -Context @{ Name = 'MySecret' }
Expand Down Expand Up @@ -84,16 +84,30 @@ function Set-Context {
$ID = $Context.ID
}
if (-not $ID) {
throw "An ID is required, either as a parameter or as a property of the context object."
throw 'An ID is required, either as a parameter or as a property of the context object.'
}
$existingContextInfo = $script:Contexts[$ID]
if (-not $existingContextInfo) {
$existingContextFile = $null
# Check if context already exists by scanning disk files
$contextFiles = Get-ChildItem -Path $script:Config.VaultPath -Filter *.json -File -Recurse
Comment thread
MariusStorhaug marked this conversation as resolved.
foreach ($file in $contextFiles) {
try {
$contextInfo = Get-Content -Path $file.FullName | ConvertFrom-Json
if ($contextInfo.ID -eq $ID) {
$existingContextFile = $file
$Path = $contextInfo.Path
break
}
} catch {
Write-Warning "Failed to read context file: $($file.FullName). Error: $_"
}
}

if (-not $existingContextFile) {
Write-Verbose "Context [$ID] not found in vault"
$Guid = [Guid]::NewGuid().ToString()
$Path = Join-Path -Path $script:Config.VaultPath -ChildPath "$Guid.json"
} else {
Write-Verbose "Context [$ID] found in vault"
$Path = $existingContextInfo.Path
}

$contextJson = ConvertTo-ContextJson -Context $Context -ID $ID
Expand All @@ -108,20 +122,6 @@ function Set-Context {
if ($PSCmdlet.ShouldProcess($ID, 'Set context')) {
Write-Verbose "Setting context [$ID] in vault"
Set-Content -Path $Path -Value $param
$content = Get-Content -Path $Path
$contextInfoObj = $content | ConvertFrom-Json
$params = @{
SealedBox = $contextInfoObj.Context
PublicKey = $script:Config.PublicKey
PrivateKey = $script:Config.PrivateKey
}
$contextObj = ConvertFrom-SodiumSealedBox @params
Write-Verbose ($contextObj | Format-List | Out-String)
$script:Contexts[$ID] = [PSCustomObject]@{
ID = $ID
Path = $Path
Context = ConvertFrom-ContextJson -JsonString $contextObj
}
}

if ($PassThru) {
Expand Down
2 changes: 1 addition & 1 deletion src/variables/private/Config.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$script:Config = [pscustomobject]@{
Initialized = $false # Has the vault been initialized?
VaultPath = Join-Path -Path $HOME -ChildPath '.contextvault' # Vault directory path
SeedShardPath = 'vault.shard' # Seed shard path (relative to VaultPath)
SeedShardPath = 'vault.shard' # Seed shard path (relative to VaultPath)
PrivateKey = $null # Private key (populated on init)
PublicKey = $null # Public key (populated on init)
}
3 changes: 0 additions & 3 deletions src/variables/private/Contexts.ps1

This file was deleted.

Loading