Skip to main content
Hcfs • 2 mins read

POST /rename_files

POST /rename_files

Atomic batch rename. Re-keys file records in the database — changes the path_hash (and optionally the encrypted / plaintext path metadata) without re-uploading the ciphertext. Every rename in the batch is signed together with one Ed25519 signature.

Use this instead of "upload at new path + delete at old path" whenever the file content hasn't changed — it's atomic, cheaper, and preserves revision history.

Authentication

Authorization: Bearer <token> matching ss58_address in the request body. See auth.md. In addition, the server verifies an Ed25519 signature over a deterministic serialization of the renames — see Signature scheme below.

Request

Method and path

POST /rename_files

Headers

Authorization: Bearer <token>
Content-Type: application/json

Body

{
"ss58_address": "5Grw...",
"folder_hash": "abc1234567890def",
"renames": [
{
"old_path_hash": [ /* 32 bytes */ ],
"new_path_hash": [ /* 32 bytes */ ],
"new_encrypted_path": [ /* variable-length ciphertext */ ],
"new_file_name": "renamed.pdf",
"new_relative_path": "Documents/2026/renamed.pdf",
"base_revision_id": [ /* 32 bytes */ ]
}
],
"signature": [ /* 64 bytes */ ],
"signing_key": [ /* 32 bytes */ ]
}
FieldTypeNotes
ss58_addressstringOwner's SS58 (accepts alias user_id)
folder_hash16-char hexFolder whose files are being renamed
renames[]arrayOne SingleRename per file to rename
signature[u8; 64]Ed25519 signature over the canonical text — see below
signing_key[u8; 32]Ed25519 public verifying key

SingleRename fields

FieldTypeNotes
old_path_hash[u8; 32]Current path_hash of the file — must exist for this (ss58, folder)
new_path_hash[u8; 32]Target path_hash — must not already exist at this (ss58, folder)
new_encrypted_pathVec<u8>New ciphertext for the encrypted path. Empty is allowed.
new_file_namestring | nullPlaintext filename for UI / progress
new_relative_pathstring | nullPlaintext relative path. Optional.
base_revision_id[u8; 32]Client's view of the file's current revision — used for optimistic concurrency per-rename

Signature scheme

The signature covers a deterministic text built from the sorted list of (old → new) hex hash pairs:

sorted_renames = renames.sort_by(r => r.old_path_hash)
pairs = sorted_renames.map(r => "hex(old_path_hash):hex(new_path_hash)")
text = "I hereby declare that I am renaming the following files on HCFS with the understanding that I have read and agree to the Terms of Service: " + pairs.join(",")
signature = Ed25519::sign(signing_key, text.as_bytes())

The server computes the same text from the received renames array and rejects mismatches. Sorting is performed server-side by old_path_hash in lexicographic byte order before verification, so the client must also sort before signing.

Response — success (200, partial-success)

Returns 200 Success even when individual renames fail — inspect the failures array.

{
"Success": {
"status": "ok",
"renamed_count": 2,
"successes": [
{
"old_path_hash": [ /* 32 bytes */ ],
"new_path_hash": [ /* 32 bytes */ ],
"new_revision_id": [ /* 32 bytes — server-assigned */ ],
"new_revision_seq": 5
}
],
"failures": [
{
"old_path_hash": [ /* 32 bytes */ ],
"reason": "revision_mismatch"
}
]
}
}
FieldMeaning
renamed_countEntries that succeeded (successes.len())
successes[]Per-rename result with the new server-assigned revision_id + revision_seq
failures[]Per-rename failure with the offending old_path_hash and a reason code

Known failure reason codes

reasonMeaning
not_foundNo row at old_path_hash for this (ss58, folder)
target_existsA row already exists at new_path_hash — pick a different target or delete the existing one first
revision_mismatchbase_revision_id does not match the current row's revision — re-fetch state and retry this entry
database_errorTransient per-entry DB failure

Response — errors (non-200)

Only request-level failures produce an Error envelope:

Statuserror codeCause
400invalid_manifest / batch_too_largeBody malformed, signature verification failed, or too many entries
401unauthorizedMissing / bad bearer token
403forbiddenToken resolves to a different SS58 than ss58_address

Example

curl -X POST "$SERVER/rename_files" \
-H "Authorization: Bearer $SS58" \
-H "Content-Type: application/json" \
-d @rename_batch.json

(Constructing rename_batch.json requires computing the signature locally — the exact format is the canonical text above, signed with the user's Ed25519 key.)

Notes

  • The signature covers the (old → new) path-hash pairs only. new_encrypted_path, new_file_name, and new_relative_path are not signed — they are metadata the server accepts on trust once the rename pair check passes.
  • Because each rename carries its own base_revision_id, entries in the same batch may be independently accepted or rejected. The rest of the batch is unaffected.
  • For a simple "move one file" the batch is allowed to have exactly one entry. There is no single-rename variant.
  • After a successful rename, the file's revision_seq increments and its revision_id changes. Store the new_revision_id from the response for the next write.