feat: encrypted secrets vault across CLI, desktop, dashboard, and backend

Local vault (apps/cli): clean-room PRMVLT01 format — Argon2id →
XChaCha20-Poly1305, AEAD-bound header, atomic writes. vault:* / secrets:*
/ inject (primora:// refs) / agent credential broker (dummy tokens in the
child, real secret on the wire) / TTL sessions / stack:* operator group.

Project vault (backend): core.project_secrets, AES-256-GCM via the
platform encryptor. Metadata-only list, audit-logged reveal, and
secret://NAME refs resolved at job delivery — refs stored, values never.

Surfaces: dashboard Vault page (+ local-vault card under the Tauri
shell), desktop vault window via IPC to the primora binary,
secrets:* --remote CLI mode, metadata/write-only MCP tools so agents
consume secrets via secret:// refs without holding plaintext.

Authored By: TDvorak <[email protected]>

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-24 18:46:37 +02:00
1 parent 31bc23386b
commit 585bb04ab2
52 files changed
+3929 -142

No files matched your search

@@ -307,6 +307,17 @@ type CoreProjectMember struct {
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type CoreProjectSecret struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Ciphertext []byte `json:"ciphertext"`
Url string `json:"url"`
Notes string `json:"notes"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type CoreScheduledJob struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
@@ -0,0 +1,181 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: project_secrets.sql
package db
import (
"context"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
const deleteProjectSecret = `-- name: DeleteProjectSecret :one
DELETE FROM core.project_secrets
WHERE project_id = $1
AND name = $2
RETURNING id
`
type DeleteProjectSecretParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) DeleteProjectSecret(ctx context.Context, arg DeleteProjectSecretParams) (uuid.UUID, error) {
row := q.db.QueryRow(ctx, deleteProjectSecret, arg.ProjectID, arg.Name)
var id uuid.UUID
err := row.Scan(&id)
return id, err
}
const getProjectSecret = `-- name: GetProjectSecret :one
SELECT id, project_id, name, ciphertext, url, notes, created_at, updated_at FROM core.project_secrets
WHERE project_id = $1
AND name = $2
`
type GetProjectSecretParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) GetProjectSecret(ctx context.Context, arg GetProjectSecretParams) (CoreProjectSecret, error) {
row := q.db.QueryRow(ctx, getProjectSecret, arg.ProjectID, arg.Name)
var i CoreProjectSecret
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.Ciphertext,
&i.Url,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listProjectSecretValues = `-- name: ListProjectSecretValues :many
SELECT name, ciphertext
FROM core.project_secrets
WHERE project_id = $1
`
type ListProjectSecretValuesRow struct {
Name string `json:"name"`
Ciphertext []byte `json:"ciphertext"`
}
func (q *Queries) ListProjectSecretValues(ctx context.Context, projectID uuid.UUID) ([]ListProjectSecretValuesRow, error) {
rows, err := q.db.Query(ctx, listProjectSecretValues, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListProjectSecretValuesRow
for rows.Next() {
var i ListProjectSecretValuesRow
if err := rows.Scan(&i.Name, &i.Ciphertext); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listProjectSecrets = `-- name: ListProjectSecrets :many
SELECT id, project_id, name, url, notes, created_at, updated_at
FROM core.project_secrets
WHERE project_id = $1
ORDER BY name ASC
`
type ListProjectSecretsRow struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Url string `json:"url"`
Notes string `json:"notes"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) ListProjectSecrets(ctx context.Context, projectID uuid.UUID) ([]ListProjectSecretsRow, error) {
rows, err := q.db.Query(ctx, listProjectSecrets, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListProjectSecretsRow
for rows.Next() {
var i ListProjectSecretsRow
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.Url,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const upsertProjectSecret = `-- name: UpsertProjectSecret :one
INSERT INTO core.project_secrets (
project_id,
name,
ciphertext,
url,
notes
) VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (project_id, name) DO UPDATE SET
ciphertext = EXCLUDED.ciphertext,
url = EXCLUDED.url,
notes = EXCLUDED.notes,
updated_at = NOW()
RETURNING id, project_id, name, ciphertext, url, notes, created_at, updated_at
`
type UpsertProjectSecretParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Ciphertext []byte `json:"ciphertext"`
Url string `json:"url"`
Notes string `json:"notes"`
}
func (q *Queries) UpsertProjectSecret(ctx context.Context, arg UpsertProjectSecretParams) (CoreProjectSecret, error) {
row := q.db.QueryRow(ctx, upsertProjectSecret,
arg.ProjectID,
arg.Name,
arg.Ciphertext,
arg.Url,
arg.Notes,
)
var i CoreProjectSecret
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Name,
&i.Ciphertext,
&i.Url,
&i.Notes,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
@@ -45,6 +45,7 @@ type Querier interface {
DeleteOrganizationByID(ctx context.Context, id uuid.UUID) (CoreOrganization, error)
DeletePendingInvitationByIDForOrganization(ctx context.Context, arg DeletePendingInvitationByIDForOrganizationParams) (CoreProjectInvitation, error)
DeleteProjectByID(ctx context.Context, id uuid.UUID) (CoreProject, error)
DeleteProjectSecret(ctx context.Context, arg DeleteProjectSecretParams) (uuid.UUID, error)
DeleteScheduledJob(ctx context.Context, arg DeleteScheduledJobParams) (uuid.UUID, error)
DeleteSetting(ctx context.Context, key string) error
DeleteWebhook(ctx context.Context, arg DeleteWebhookParams) (CoreWebhook, error)
@@ -68,6 +69,7 @@ type Querier interface {
GetProjectByID(ctx context.Context, id uuid.UUID) (CoreProject, error)
GetProjectMembership(ctx context.Context, arg GetProjectMembershipParams) (GetProjectMembershipRow, error)
GetProjectOverview(ctx context.Context, id uuid.UUID) (GetProjectOverviewRow, error)
GetProjectSecret(ctx context.Context, arg GetProjectSecretParams) (CoreProjectSecret, error)
GetScheduledJobByID(ctx context.Context, id uuid.UUID) (CoreScheduledJob, error)
GetSetting(ctx context.Context, key string) (GetSettingRow, error)
GetUserByAuthSubject(ctx context.Context, authSubject string) (CoreUser, error)
@@ -96,6 +98,8 @@ type Querier interface {
ListOrganizationsForUser(ctx context.Context, userID uuid.UUID) ([]ListOrganizationsForUserRow, error)
ListPendingWebhookDeliveries(ctx context.Context, limit int32) ([]CoreWebhookDelivery, error)
ListProjectMembers(ctx context.Context, projectID uuid.UUID) ([]ListProjectMembersRow, error)
ListProjectSecretValues(ctx context.Context, projectID uuid.UUID) ([]ListProjectSecretValuesRow, error)
ListProjectSecrets(ctx context.Context, projectID uuid.UUID) ([]ListProjectSecretsRow, error)
ListProjectsForOrganization(ctx context.Context, arg ListProjectsForOrganizationParams) ([]ListProjectsForOrganizationRow, error)
ListScheduledJobRuns(ctx context.Context, arg ListScheduledJobRunsParams) ([]CoreScheduledJobRun, error)
ListScheduledJobs(ctx context.Context, projectID uuid.UUID) ([]CoreScheduledJob, error)
@@ -135,6 +139,7 @@ type Querier interface {
UpdateScheduledJob(ctx context.Context, arg UpdateScheduledJobParams) (CoreScheduledJob, error)
UpdateWebhook(ctx context.Context, arg UpdateWebhookParams) (CoreWebhook, error)
UpsertComponent(ctx context.Context, arg UpsertComponentParams) (CoreComponent, error)
UpsertProjectSecret(ctx context.Context, arg UpsertProjectSecretParams) (CoreProjectSecret, error)
UpsertSetting(ctx context.Context, arg UpsertSettingParams) (CoreSetting, error)
UpsertUser(ctx context.Context, arg UpsertUserParams) (CoreUser, error)
}