namespace DodoSSH.Domain;
///
/// An SSH host.
///
///
///
/// The only vault item type in M1. Everything sensitive — username, notes, jump chain, SSH
/// options — lives inside . There is deliberately no plaintext label:
/// access-control administration happens in the client, which can decrypt names, so the server
/// never needs a searchable title.
///
///
/// and are the one deliberate plaintext concession, and
/// only when is set. The relay must resolve its target server-side or
/// it becomes an authenticated open TCP proxy into the operator's own network. A database CHECK
/// constraint enforces the pairing so it cannot drift. See ADR 0004.
///
///
public sealed class SshHost : IVaultItem
{
/// Primary key. UUIDv7, generated by the client so items can be created offline.
public Guid Id { get; set; }
/// Owning vault.
public Guid VaultId { get; set; }
/// Owning vault.
public Vault? Vault { get; set; }
/// The encrypted item: a DSH1 envelope. Opaque to the server.
public byte[] Payload { get; set; } = [];
/// The item's data key, wrapped under the vault key. Opaque.
public byte[]? DataKeyWrap { get; set; }
///
/// Reserved for per-item content keys wrapped to individual users, which is what will make
/// per-item access control cryptographic rather than server-enforced. Present from the first
/// migration so that lands without a migration; see docs/crypto.md §3.
///
public Guid? ContentKeyId { get; set; }
/// Vault key generation this payload was encrypted under.
public int KeyGeneration { get; set; }
/// AAD rule version, enabling a lazy re-encrypt-on-write migration later.
public short PayloadAadVersion { get; set; }
/// Whether this host may be dialled through the server relay.
public bool RelayEnabled { get; set; }
/// Target hostname. Permitted only when is set.
public string? Hostname { get; set; }
/// Target port. Permitted only when is set.
public int? Port { get; set; }
/// Owning group, for tree placement. Groups arrive in M2.
public Guid? GroupId { get; set; }
///
/// Client-visible, monotonic item version. Used for optimistic concurrency on push, and
/// deliberately distinct from the internal xmin guard, which is never exposed because
/// it is not stable across VACUUM FREEZE.
///
public int Version { get; set; }
/// Latest change-log sequence touching this row, so a delta pull can join directly.
public long ChangeSequence { get; set; }
/// Creation timestamp.
public DateTimeOffset CreatedAtUtc { get; set; }
/// Last modification timestamp.
public DateTimeOffset UpdatedAtUtc { get; set; }
///
/// Soft-delete marker. Deletes are tombstones: a client that has been offline must be able to
/// learn an item went away, and a vanished row is indistinguishable from one never seen.
///
public DateTimeOffset? DeletedAtUtc { get; set; }
/// Who created it.
public Guid CreatedByUserId { get; set; }
/// Who last modified it.
public Guid UpdatedByUserId { get; set; }
}
///
/// An SSH key pair, held as ciphertext.
///
///
///
/// The private key, its passphrase, its label and its comment are all inside . This
/// row is the reason the vault is worth having — a key that syncs between a user's machines and can later be
/// shared with a teammate — and it is also the row that would hurt most if the server could read it, so
/// there is deliberately not one plaintext column of substance.
///
///
/// Notably absent: the relay trio. A key is not something the server dials, so the plaintext concession
/// ADR 0004 makes for has no analogue here and no reason to exist. That is the whole
/// argument for a separate table rather than one wide item table: the columns a host needs are columns a key
/// must never have, and a shared table could only make them nullable and trust the code.
///
///
/// is the single exception, and it stays null until something needs it.
/// The contract reserved the slot for showing which key a host is configured to use without decrypting
/// every key first; write it only when that feature lands, and never derive anything security-relevant from
/// it, because a fingerprint the server stores is a fingerprint the server chose.
///
///
public sealed class VaultSshKey : IVaultItem
{
/// Primary key. UUIDv7, generated by the client so keys can be created offline.
public Guid Id { get; set; }
/// Owning vault.
public Guid VaultId { get; set; }
/// Owning vault.
public Vault? Vault { get; set; }
/// The encrypted key material: a DSH1 envelope. Opaque to the server.
public byte[] Payload { get; set; } = [];
/// The item's data key, wrapped under the vault key. Opaque.
public byte[]? DataKeyWrap { get; set; }
/// Reserved for per-item content keys wrapped to individual users; see docs/crypto.md §3.
public Guid? ContentKeyId { get; set; }
/// Vault key generation this payload was encrypted under.
public int KeyGeneration { get; set; }
/// AAD rule version, enabling a lazy re-encrypt-on-write migration later.
public short PayloadAadVersion { get; set; }
///
/// OpenSSH-style SHA256:base64 fingerprint of the public half, when a client publishes it.
///
///
/// Plaintext, and therefore opt-in and currently unused. A public key fingerprint is not a secret, but
/// it is an identifier that links a vault to a machine's authorized_keys, so it is stored only
/// when a feature needs it rather than because it is harmless.
///
public string? PublicKeyFingerprint { get; set; }
/// Client-visible, monotonic item version, used for expectedVersion checks.
public int Version { get; set; }
/// Latest change-log sequence touching this row, so a delta pull can join directly.
public long ChangeSequence { get; set; }
/// Creation timestamp.
public DateTimeOffset CreatedAtUtc { get; set; }
/// Last modification timestamp.
public DateTimeOffset UpdatedAtUtc { get; set; }
/// Soft-delete marker; a tombstone, so an offline client learns the key went away.
public DateTimeOffset? DeletedAtUtc { get; set; }
/// Who created it.
public Guid CreatedByUserId { get; set; }
/// Who last modified it.
public Guid UpdatedByUserId { get; set; }
}
///
/// A stored username and password, as ciphertext.
///
///
///
/// Its own table for the same reason is: the columns a host needs are columns a
/// credential must never have. There is no relay trio here, and unlike a key there is not even a fingerprint
/// — nothing about a password is safe to hold in the clear, not its length, not a hash, not a hint. So this
/// row is an opaque envelope and its bookkeeping, and that is the whole design.
///
///
/// The server cannot enforce anything about the contents, and should not pretend to. Whether a
/// credential has a username, whether its password is empty, whether it is still valid — all of that is
/// inside the payload and belongs to the client. The one thing this row asserts is that the ciphertext
/// belongs to a vault and carries a version, which is what the write path needs to order changes.
///
///
public sealed class VaultCredential : IVaultItem
{
/// Primary key. UUIDv7, generated by the client so credentials can be created offline.
public Guid Id { get; set; }
/// Owning vault.
public Guid VaultId { get; set; }
/// Owning vault.
public Vault? Vault { get; set; }
/// The encrypted credential: a DSH1 envelope. Opaque to the server.
public byte[] Payload { get; set; } = [];
/// The item's data key, wrapped under the vault key. Opaque.
public byte[]? DataKeyWrap { get; set; }
/// Reserved for per-item content keys wrapped to individual users; see docs/crypto.md §3.
public Guid? ContentKeyId { get; set; }
/// Vault key generation this payload was encrypted under.
public int KeyGeneration { get; set; }
/// AAD rule version, enabling a lazy re-encrypt-on-write migration later.
public short PayloadAadVersion { get; set; }
/// Client-visible, monotonic item version, used for expectedVersion checks.
public int Version { get; set; }
/// Latest change-log sequence touching this row, so a delta pull can join directly.
public long ChangeSequence { get; set; }
/// Creation timestamp.
public DateTimeOffset CreatedAtUtc { get; set; }
/// Last modification timestamp.
public DateTimeOffset UpdatedAtUtc { get; set; }
/// Soft-delete marker; a tombstone, so an offline client learns the credential went away.
public DateTimeOffset? DeletedAtUtc { get; set; }
/// Who created it.
public Guid CreatedByUserId { get; set; }
/// Who last modified it.
public Guid UpdatedByUserId { get; set; }
}