๐Ÿ” Code Extractor

class RootCleaner

Maturity: 46

A class that completely clears the reMarkable cloud's root.docSchema file, removing all document references while maintaining the proper file structure and version.

File:
/tf/active/vicechatdev/e-ink-llm/cloudtest/clear_root_docschema.py
Lines:
47 - 257
Complexity:
complex

Purpose

RootCleaner provides a safe and verified method to completely empty the reMarkable cloud storage by clearing the root.docSchema file. It handles authentication, retrieves current root state, creates an empty root structure (containing only the version line '3'), uploads it using the working reMarkable sync API endpoints, and verifies the operation succeeded. This is useful for resetting cloud storage or testing purposes.

Source Code

class RootCleaner:
    """Clears root.docSchema using the working upload mechanism"""
    
    def __init__(self):
        # Load auth session
        auth = RemarkableAuth()
        self.session = auth.get_authenticated_session()
        
        if not self.session:
            raise RuntimeError("Failed to authenticate with reMarkable")
        
        print("๐Ÿงน Root Cleaner Initialized")
    
    def get_current_root_info(self):
        """Get current root.docSchema info using working method"""
        print("\n๐Ÿ“‹ Step 1: Getting current root.docSchema...")
        
        # Get root info
        root_response = self.session.get("https://eu.tectonic.remarkable.com/sync/v4/root")
        root_response.raise_for_status()
        root_data = root_response.json()
        
        # Get root content
        root_content_response = self.session.get(f"https://eu.tectonic.remarkable.com/sync/v3/files/{root_data['hash']}")
        root_content_response.raise_for_status()
        root_content = root_content_response.text
        
        print(f"โœ… Current root hash: {root_data['hash']}")
        print(f"โœ… Current generation: {root_data.get('generation')}")
        print(f"โœ… Root content size: {len(root_content)} bytes")
        print(f"๐Ÿ“„ Current root content:")
        print(f"   {repr(root_content)}")
        
        # Count current documents
        lines = root_content.strip().split('\n')
        doc_count = len(lines) - 1  # Subtract version line
        print(f"๐Ÿ“Š Documents currently in root: {doc_count}")
        
        return root_data, root_content
    
    def create_empty_root(self):
        """Create completely empty root.docSchema with only version line"""
        print(f"\n๐Ÿงน Step 2: Creating empty root.docSchema...")
        
        # Empty root content: just version "3" and newline
        empty_root_content = "3\n"
        
        print(f"โœ… Empty root content: {repr(empty_root_content)}")
        print(f"โœ… Empty root size: {len(empty_root_content)} bytes")
        
        return empty_root_content
    
    def upload_empty_root(self, empty_root_content: str, current_generation: int):
        """Upload empty root.docSchema and update roothash using WORKING method"""
        print(f"\nโฌ†๏ธ Step 3: Uploading empty root.docSchema...")
        
        # Calculate hash for empty root
        root_hash = hashlib.sha256(empty_root_content.encode()).hexdigest()
        print(f"โœ… New empty root hash: {root_hash}")
        
        # Upload root content using WORKING method from test_move_from_trash.py
        headers = {
            'Content-Type': 'text/plain',
            'rm-batch-number': '1',
            'rm-filename': 'root.docSchema',  # System filename for root.docSchema
            'rm-sync-id': str(uuid.uuid4()),
            'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'en-BE,*',
            'Connection': 'Keep-Alive'
        }
        
        # Add CRC32C checksum
        crc32c_header = compute_crc32c_header(empty_root_content.encode())
        if crc32c_header:
            headers['x-goog-hash'] = crc32c_header
        
        print(f"๐Ÿ“ค PUT to: https://eu.tectonic.remarkable.com/sync/v3/files/{root_hash}")
        print(f"   Headers: {list(headers.keys())}")
        
        upload_response = self.session.put(
            f"https://eu.tectonic.remarkable.com/sync/v3/files/{root_hash}",  # WORKING ENDPOINT
            data=empty_root_content.encode(),
            headers=headers
        )
        
        print(f"โœ… Root content upload response: {upload_response.status_code}")
        if upload_response.status_code not in [200, 202]:
            print(f"โŒ Upload failed: {upload_response.text}")
            raise RuntimeError(f"Root content upload failed: {upload_response.status_code}")
        
        # Update root hash pointer using WORKING method
        print(f"\n๐Ÿ”„ Step 4: Updating root hash pointer...")
        
        # Create root data exactly like working upload_manager.py
        root_update_data = {
            "broadcast": True,
            "generation": current_generation,  # Use current generation, don't increment
            "hash": root_hash
        }
        
        # Convert to JSON with 2-space indent like real app
        root_content_body = json.dumps(root_update_data, indent=2).encode('utf-8')
        
        print(f"โœ… Root update data:")
        print(f"   Generation: {current_generation} (keeping current)")
        print(f"   Hash: {root_hash}")
        
        # Headers exactly like working upload_manager.py
        headers = {
            'Content-Type': 'application/json',
            'rm-batch-number': '1',
            'rm-filename': 'roothash',
            'rm-sync-id': str(uuid.uuid4()),
            'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'en-BE,*',
            'Connection': 'Keep-Alive'
        }
        
        # Add CRC32C checksum
        crc32c_header = compute_crc32c_header(root_content_body)
        if crc32c_header:
            headers['x-goog-hash'] = crc32c_header
        
        # Use /sync/v3/root endpoint like working code
        print(f"๐Ÿ“ค PUT to: https://eu.tectonic.remarkable.com/sync/v3/root")
        
        root_update_response = self.session.put(
            "https://eu.tectonic.remarkable.com/sync/v3/root",  # WORKING ENDPOINT
            data=root_content_body,
            headers=headers
        )
        
        print(f"โœ… Root update response: {root_update_response.status_code}")
        if root_update_response.status_code not in [200, 202]:
            print(f"โŒ Root update failed: {root_update_response.text}")
            raise RuntimeError(f"Root update failed: {root_update_response.status_code}")
        
        return root_hash
    
    def verify_empty_root(self):
        """Verify that the root is now empty"""
        print(f"\n๐Ÿ” Step 5: Verifying empty root...")
        
        try:
            # Get updated root info
            root_response = self.session.get("https://eu.tectonic.remarkable.com/sync/v4/root")
            root_response.raise_for_status()
            root_data = root_response.json()
            
            # Get root content
            root_content_response = self.session.get(f"https://eu.tectonic.remarkable.com/sync/v3/files/{root_data['hash']}")
            root_content_response.raise_for_status()
            root_content = root_content_response.text
            
            print(f"โœ… Verification - New root hash: {root_data['hash']}")
            print(f"โœ… Verification - New generation: {root_data.get('generation')}")
            print(f"โœ… Verification - Root content size: {len(root_content)} bytes")
            print(f"๐Ÿ“„ Verification - Root content: {repr(root_content)}")
            
            # Check if truly empty
            lines = root_content.strip().split('\n')
            doc_count = len(lines) - 1  # Subtract version line
            
            if doc_count == 0 and root_content.strip() == "3":
                print(f"๐ŸŽ‰ SUCCESS: Root is completely empty!")
                print(f"   Only version line '3' remains")
                print(f"   Document count: 0")
                return True
            else:
                print(f"โš ๏ธ Root not completely empty:")
                print(f"   Document count: {doc_count}")
                print(f"   Content: {repr(root_content)}")
                return False
                
        except Exception as e:
            print(f"โŒ Verification failed: {e}")
            return False
    
    def clear_root_completely(self):
        """Complete process to clear root.docSchema"""
        print(f"๐Ÿงน Clearing Root DocSchema Completely")
        print("=" * 50)
        
        try:
            # Step 1: Get current root info
            root_data, root_content = self.get_current_root_info()
            
            # Step 2: Create empty root
            empty_root_content = self.create_empty_root()
            
            # Step 3-4: Upload empty root and update pointer
            new_root_hash = self.upload_empty_root(empty_root_content, root_data['generation'])
            
            # Step 5: Verify result
            verification_success = self.verify_empty_root()
            
            if verification_success:
                print(f"\n๐ŸŽ‰ SUCCESS! Root cleared completely")
                print(f"   New root hash: {new_root_hash}")
                print(f"   Cloud is now completely empty from user perspective")
                print(f"   All documents have been removed from root.docSchema")
                return True
            else:
                print(f"\nโš ๏ธ Root clearing completed but verification failed")
                return False
            
        except Exception as e:
            print(f"\nโŒ Root clearing failed: {e}")
            return False

Parameters

Name Type Default Kind
bases - -

Parameter Details

__init__: No parameters required. The constructor automatically initializes authentication with reMarkable cloud services using RemarkableAuth and establishes an authenticated session. Raises RuntimeError if authentication fails.

Return Value

Instantiation returns a RootCleaner object with an authenticated session. The main method clear_root_completely() returns a boolean: True if root was successfully cleared and verified, False otherwise. Individual methods return: get_current_root_info() returns tuple (root_data dict, root_content string), create_empty_root() returns string '3\n', upload_empty_root() returns string (new root hash), verify_empty_root() returns boolean.

Class Interface

Methods

__init__(self)

Purpose: Initialize RootCleaner with authenticated reMarkable session

Returns: None - initializes instance with self.session attribute

get_current_root_info(self) -> tuple[dict, str]

Purpose: Retrieve current root.docSchema information including hash, generation, and content

Returns: Tuple of (root_data dict containing 'hash' and 'generation', root_content string with document list)

create_empty_root(self) -> str

Purpose: Create an empty root.docSchema content with only the version line

Returns: String '3\n' representing empty root with version 3

upload_empty_root(self, empty_root_content: str, current_generation: int) -> str

Purpose: Upload empty root content to reMarkable cloud and update root hash pointer

Parameters:

  • empty_root_content: The empty root content string (typically '3\n')
  • current_generation: Current generation number from root metadata to preserve

Returns: String containing the SHA256 hash of the uploaded empty root content

verify_empty_root(self) -> bool

Purpose: Verify that the root.docSchema is now empty by fetching and checking current state

Returns: Boolean: True if root contains only version line '3' with 0 documents, False otherwise

clear_root_completely(self) -> bool

Purpose: Execute complete workflow to clear root.docSchema: get current state, create empty root, upload, and verify

Returns: Boolean: True if entire clearing process succeeded and was verified, False if any step failed

Attributes

Name Type Description Scope
session requests.Session Authenticated HTTP session for making API requests to reMarkable cloud services, initialized by RemarkableAuth instance

Dependencies

  • json
  • time
  • hashlib
  • uuid
  • base64
  • zlib
  • pathlib
  • crc32c

Required Imports

import json
import time
import hashlib
import uuid
import base64
import zlib
from pathlib import Path
from auth import RemarkableAuth
import crc32c

Usage Example

# Basic usage to clear root completely
cleaner = RootCleaner()
success = cleaner.clear_root_completely()
if success:
    print('Root cleared successfully')

# Step-by-step usage with individual methods
cleaner = RootCleaner()

# Get current state
root_data, root_content = cleaner.get_current_root_info()
print(f'Current generation: {root_data["generation"]}')

# Create empty root
empty_content = cleaner.create_empty_root()

# Upload and update
new_hash = cleaner.upload_empty_root(empty_content, root_data['generation'])

# Verify
if cleaner.verify_empty_root():
    print('Verification passed')

Best Practices

  • Always instantiate RootCleaner in a try-except block to handle authentication failures gracefully
  • Use clear_root_completely() for the full workflow rather than calling individual methods unless you need fine-grained control
  • The class maintains state through self.session - do not modify this attribute directly
  • Methods should be called in order: get_current_root_info() -> create_empty_root() -> upload_empty_root() -> verify_empty_root()
  • The clear_root_completely() method orchestrates all steps automatically and includes error handling
  • This operation is destructive - all document references in root.docSchema will be removed from cloud
  • The class uses the working reMarkable sync v3/v4 API endpoints with proper headers and checksums
  • Generation numbers are preserved (not incremented) to match reMarkable's expected behavior
  • Each method prints detailed progress information for debugging and monitoring
  • Verification step is critical - always check the return value to ensure operation succeeded

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function main_v23 84.1% similar

    Clears all document references from the reMarkable Cloud root.docSchema, making the cloud interface appear completely empty while preserving the actual documents.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/clear_root_docschema.py
  • function simple_move_to_trash 75.2% similar

    Moves all documents from the reMarkable tablet's root directory to trash by uploading an empty root.docSchema file and updating the roothash.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/simple_clean_root.py
  • function verify_cloud_empty 75.0% similar

    Verifies that a reMarkable cloud storage account is completely empty by authenticating, retrieving the root document schema, and analyzing its contents for any remaining documents or folders.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/verify_cloud_empty.py
  • class RootDocSchemaRepair 74.1% similar

    A repair tool for fixing corrupted root.docSchema entries in reMarkable cloud storage by recalculating document sizes and rebuilding the schema.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/fix_root_docschema.py
  • function show_current_root 72.8% similar

    Fetches and displays the current root.docSchema from the reMarkable cloud sync service, showing metadata and analyzing document entries.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/show_current_root.py
โ† Back to Browse