Skip to content

Developer Guide: Programmatic Access to Pintheon Node ​

This guide will walk you through the process of programmatically interacting with a Pintheon node, including generating access tokens and uploading files.

Prerequisites ​

  1. Python 3.8 or higher installed
  2. A running Pintheon node on your local machine (default port: 3000)
  3. requests library for HTTP requests

Installation ​

First, install the required package:

bash
pip install requests

Step 1: Get an Access Token ​

To interact with the Pintheon node's API, you'll need an access token. This token is used to authenticate your requests.

Get Your Access Token ​

  1. Open the Pintheon web interface in your browser
  2. Navigate to Settings > Access Tokens
  3. Click Generate New Token
  4. Copy the generated token - you'll use this in your API requests

For detailed instructions with screenshots, see the Access Tokens documentation.

Security Notes ​

  • Keep your access token secure and never commit it to version control
  • Use environment variables to store your token in production
  • Rotate your tokens regularly for security
  • The access token provides full access to your Pintheon node's API, so treat it like a password

Step 2: Using the Access Token ​

Once you have your access token, you can use it to authenticate your API requests to the Pintheon node. The token should be included in the access_token parameter of your requests.

Step 4: Upload a File Programmatically ​

Now you can use the access token to upload files to your Pintheon node. The Pintheon node provides an API endpoint for file uploads that requires a valid access token.

python
import requests
import os

# Pintheon node configuration
PINTHEON_API_URL = "https://localhost:9999/api_upload"  # Default API endpoint

# File to upload
file_path = "path/to/your/file.txt"

# Your access token from the Pintheon web interface
# Go to Settings > Access Tokens to generate one
access_token = "your_access_token_here"  # Replace with your actual access token

# 2. Prepare the file upload
try:
    # Open the file in binary mode
    with open(file_path, 'rb') as f:
        # Prepare the multipart form data
        files = {
            'file': (os.path.basename(file_path), f, 'application/octet-stream')
        }
        
        # Required form data
        data = {
            'access_token': access_token,
            'encrypted': 'false'  # Set to 'true' for encrypted uploads
        }
        
        # 3. Make the upload request
        response = requests.post(
            PINTHEON_API_URL,
            files=files,
            data=data,
            verify=False,  # Only for local development with self-signed certs
            timeout=30  # 30 second timeout
        )
        
        # 4. Handle the response
        if response.status_code == 200:
            result = response.json()
            cid = result.get('Hash') or result.get('IpfsHash')
            if cid:
                # Construct the gateway URL
                gateway_url = f"https://your-pintheon-gateway/ipfs/{cid}"
                print(f"File uploaded successfully!")
                print(f"IPFS CID: {cid}")
                print(f"Gateway URL: {gateway_url}")
                
                # The file is now available at the gateway URL
                # You can use this URL to access the file from any IPFS gateway
            else:
                print("Upload successful but no CID returned in response")
                print(f"Response: {response.text}")
        else:
            print(f"Upload failed with status {response.status_code}")
            print(f"Response: {response.text}")
            
except FileNotFoundError:
    print(f"Error: File not found at {file_path}")
except requests.exceptions.RequestException as e:
    print(f"Network error during upload: {e}")
except Exception as e:
    print(f"An unexpected error occurred: {e}")

Important Notes: ​

  1. Access Token: The access token is generated fresh for each upload with a short expiration time (5 minutes in this example).

  2. HTTPS: The example uses HTTPS with certificate verification disabled (verify=False), which is only suitable for local development. For production, use proper SSL certificates.

  3. Gateway URL: Replace your-pintheon-gateway with your actual Pintheon node's gateway address.

  4. Error Handling: The example includes basic error handling, but you may want to enhance it based on your application's needs.

  5. File Size: For large files, consider implementing progress tracking and chunked uploads.

  6. Security: Never expose your private key in client-side code. This example is for server-side use only.

Step 5: Retrieve File Information ​

You can retrieve information about uploaded files using the Pintheon node's API. Here's how to get file information and access your uploaded files:

python
def get_file_info(ipfs_cid, pintheon_gateway="your-pintheon-gateway"):
    """
    Retrieve information about an uploaded file using its IPFS CID.
    
    :param ipfs_cid: The IPFS Content Identifier (CID) of the file
    :param pintheon_gateway: The base URL of your Pintheon gateway
    :return: Dictionary containing file information or None on error
    """
    try:
        # Construct the URL to the file on the Pintheon gateway
        file_url = f"https://{pintheon_gateway}/ipfs/{ipfs_cid}"
        
        # Make a HEAD request to get file metadata
        response = requests.head(file_url, allow_redirects=True, timeout=10)
        
        if response.status_code == 200:
            # Extract relevant headers
            file_info = {
                'cid': ipfs_cid,
                'url': file_url,
                'content_type': response.headers.get('Content-Type'),
                'content_length': response.headers.get('Content-Length'),
                'last_modified': response.headers.get('Last-Modified'),
                'etag': response.headers.get('ETag')
            }
            return file_info
        else:
            print(f"Error retrieving file info: {response.status_code}")
            return None
            
    except requests.exceptions.RequestException as e:
        print(f"Network error retrieving file info: {e}")
        return None

# Example usage
if __name__ == "__main__":
    # Replace with an actual CID from your upload
    cid = "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco"
    
    file_info = get_file_info(cid, "your-pintheon-gateway")
    if file_info:
        print("File Information:")
        for key, value in file_info.items():
            print(f"{key}: {value}")

Accessing Your Files ​

Once uploaded, your files are accessible through any IPFS gateway using the CID returned during upload. The URL format is:

https://<pintheon-gateway>/ipfs/<cid>

For example, if your Pintheon gateway is gateway.pintheon.example.com and your file's CID is QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco, the URL would be:

https://gateway.pintheon.example.com/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco

Important Notes: ​

  1. Persistence: Files are only available as long as they are pinned by your Pintheon node. Make sure to pin important files through the Pintheon web interface or API.

  2. Caching: The Pintheon gateway may cache files for performance. Changes to a file with the same CID will not be reflected until the cache expires.

  3. Performance: For large files, consider using a dedicated IPFS client for better performance when retrieving files.

Error Handling ​

When working with the Pintheon API, it's important to handle errors gracefully. Here's how to handle common error scenarios:

python
def upload_file(api_url, file_path, access_token, encrypted=False, timeout=30):
    """
    Upload a file to a Pintheon node with proper error handling.
    
    :param api_url: URL of the Pintheon API endpoint (e.g., 'https://localhost:9999/api_upload')
    :param file_path: Path to the file to upload
    :param access_token: Valid access token for authentication
    :param encrypted: Whether to encrypt the file (True/False)
    :param timeout: Request timeout in seconds
    :return: Tuple of (success: bool, result: dict or str)
    """
    try:
        # Verify file exists and is readable
        if not os.path.isfile(file_path):
            return False, f"File not found: {file_path}"
            
        if not os.access(file_path, os.R_OK):
            return False, f"Permission denied reading file: {file_path}"
        
        # Prepare the request
        with open(file_path, 'rb') as f:
            files = {'file': (os.path.basename(file_path), f, 'application/octet-stream')}
            data = {
                'access_token': access_token,
                'encrypted': str(encrypted).lower()
            }
            
            # Make the request
            response = requests.post(
                api_url,
                files=files,
                data=data,
                verify=False,  # Disable SSL verification for local development
                timeout=timeout
            )
            
            # Handle the response
            if response.status_code == 200:
                try:
                    result = response.json()
                    if 'Hash' in result or 'IpfsHash' in result:
                        return True, result
                    else:
                        return False, f"Unexpected response format: {result}"
                except ValueError:
                    return False, f"Invalid JSON response: {response.text}"
                    
            elif response.status_code == 400:
                return False, f"Bad request: {response.text}"
                
            elif response.status_code == 401:
                return False, "Authentication failed: Invalid or expired access token"
                
            elif response.status_code == 403:
                return False, "Forbidden: Check your token permissions"
                
            elif response.status_code == 413:
                return False, "File too large: Exceeds maximum allowed size"
                
            elif response.status_code == 500:
                return False, f"Server error: {response.text}"
                
            else:
                return False, f"Unexpected status code {response.status_code}: {response.text}
                
    except requests.exceptions.Timeout:
        return False, "Request timed out. The server took too long to respond."
        
    except requests.exceptions.SSLError:
        return False, "SSL certificate verification failed. For development, you can disable verification (not recommended for production)."
        
    except requests.exceptions.ConnectionError:
        return False, "Connection failed. Check if the Pintheon node is running and accessible."
        
    except requests.exceptions.RequestException as e:
        return False, f"Request failed: {str(e)}"
        
    except IOError as e:
        return False, f"File operation failed: {str(e)}"
        
    except Exception as e:
        return False, f"An unexpected error occurred: {str(e)}"

Example Usage with Error Handling ​

python
# Example usage of the upload_file function
api_url = "https://localhost:9999/api_upload"
file_path = "path/to/your/file.txt"
access_token = "your_access_token_here"

success, result = upload_file(api_url, file_path, access_token)

if success:
    cid = result.get('Hash') or result.get('IpfsHash')
    print(f"File uploaded successfully! CID: {cid}")
    
    # You can now access the file at:
    gateway_url = f"https://your-pintheon-gateway/ipfs/{cid}"
    print(f"Access your file at: {gateway_url}")
else:
    print(f"Upload failed: {result}")
    
    # Handle specific error cases
    if "Authentication failed" in result:
        print("Please check your access token and try again.")
    elif "Connection failed" in result:
        print("Please ensure the Pintheon node is running and accessible.")

Common Error Scenarios ​

  1. Authentication Errors (401/403)

    • Token has expired (generate a new one)
    • Invalid token format
    • Missing required permissions
    • Public key not registered with the node
  2. File Upload Errors

    • File too large (413)
    • Invalid file format
    • Insufficient disk space on node
    • Network issues during upload
  3. Server Errors (5xx)

    • Pintheon node service down
    • Database connection issues
    • Internal server errors

Best Practices ​

1. Token Management ​

  • Short-Lived Tokens: Generate tokens with short expiration times (5-15 minutes)
  • Token Storage: Store tokens securely using environment variables or a secrets management system
  • Token Rotation: Implement token rotation to automatically refresh tokens before they expire
  • Least Privilege: Only request the minimum permissions needed for your application

2. File Handling ​

  • File Validation:

    • Verify file types and sizes before upload
    • Implement virus scanning for user-uploaded content
    • Set reasonable file size limits
  • Large Files:

    • For files > 100MB, consider chunked uploads
    • Implement progress tracking for better user feedback
    • Handle network interruptions gracefully with resume capabilities

3. Performance Optimization ​

  • Connection Pooling: Reuse HTTP connections for multiple requests
  • Timeouts: Set appropriate timeouts for all API calls
  • Concurrent Uploads: For multiple files, use concurrent uploads with thread pools
  • Caching: Cache frequently accessed files locally when possible

4. Security ​

  • HTTPS: Always use HTTPS in production
  • Input Validation: Sanitize all user inputs
  • Error Messages: Don't expose sensitive information in error messages
  • Rate Limiting: Implement client-side rate limiting to avoid being throttled

5. Monitoring and Logging ​

  • Request Logging: Log all API requests and responses (redacting sensitive data)
  • Error Tracking: Implement comprehensive error tracking
  • Performance Metrics: Monitor upload/download speeds and failure rates
  • Alerting: Set up alerts for abnormal conditions (e.g., high error rates)

Troubleshooting ​

1. Authentication Issues ​

Symptoms:

  • HTTP 401 (Unauthorized) or 403 (Forbidden) errors
  • "Invalid token" or "Token expired" messages
  • "Public key not registered" errors

Solutions:

  • Token Expired: Generate a new access token with hvym_stellar

    python
    from hvym_stellar import StellarSharedKeyTokenBuilder
    from stellar_sdk import Keypair
    
    # Generate a new token
    sender = Keypair.from_secret("your_secret_key")
    token = StellarSharedKeyTokenBuilder(
        sender=sender,
        receiver_public_key="pintheon_node_public_key",
        token_type="access",
        expires_in=300  # 5 minutes
    )
    new_token = token.serialize()
  • Public Key Not Registered:

    1. Get your public key: print(sender.public_key)
    2. Register it in the Pintheon web interface under Settings > Access Tokens
  • Insufficient Permissions:

    • Verify the token was created with the required permissions
    • Check the token's caveats for any restrictions

2. Connection Problems ​

Symptoms:

  • Connection timeouts
  • "Connection refused" errors
  • SSL certificate verification failures

Solutions:

  • Verify Node Status:

    bash
    # Check if the Pintheon service is running
    docker ps | grep pintheon
    
    # Check service logs
    docker logs pintheon-node
  • Check Network Connectivity:

    bash
    # Test basic connectivity
    ping your-pintheon-host
    
    # Test port accessibility
    telnet your-pintheon-host 9999
    
    # Test HTTPS connectivity
    curl -v https://your-pintheon-host:9999/api_upload
  • SSL Certificate Issues:

    • For development, you can disable SSL verification (not recommended for production):
      python
      import requests
      requests.packages.urllib3.disable_warnings()  # Suppress SSL warnings
      
      response = requests.get(
          "https://your-pintheon-host:9999/api_endpoint",
          verify=False  # Disable SSL verification
      )
    • For production, ensure you have valid SSL certificates configured

3. File Upload Problems ​

Symptoms:

  • Slow uploads
  • Timeout errors
  • "File too large" errors
  • Corrupted files

Solutions:

  • Large Files:

    • For files > 100MB, implement chunked uploads
    • Increase timeout values:
      python
      response = requests.post(
          "https://your-pintheon-host:9999/api_upload",
          files=files,
          data=data,
          timeout=300  # 5 minute timeout
      )
  • File Size Limits:

    • Check available disk space on the Pintheon node:
      bash
      df -h /path/to/ipfs/storage
    • Check IPFS repo size and quota:
      bash
      ipfs repo stat
  • Corrupted Files:

    • Verify file integrity after upload using checksums
    • Implement retry logic for failed uploads

4. Performance Issues ​

Symptoms:

  • Slow response times
  • High CPU/memory usage
  • Timeout errors

Optimizations:

  • Connection Pooling:

    python
    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.util.retry import Retry
    
    session = requests.Session()
    retry_strategy = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[500, 502, 503, 504]
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    
    # Use this session for all requests
    response = session.post("https://your-pintheon-host:9999/api_upload", files=files, data=data)
  • Concurrent Uploads:

    python
    from concurrent.futures import ThreadPoolExecutor, as_completed
    
    def upload_file_wrapper(file_path):
        return upload_file(API_URL, file_path, ACCESS_TOKEN)
    
    # Upload up to 5 files concurrently
    with ThreadPoolExecutor(max_workers=5) as executor:
        future_to_file = {
            executor.submit(upload_file_wrapper, file_path): file_path 
            for file_path in file_paths
        }
        
        for future in as_completed(future_to_file):
            file_path = future_to_file[future]
            try:
                success, result = future.result()
                if success:
                    print(f"Uploaded {file_path}: {result.get('Hash')}")
                else:
                    print(f"Failed to upload {file_path}: {result}")
            except Exception as e:
                print(f"Error uploading {file_path}: {str(e)}")

5. Common Error Messages ​

  • "Invalid access token":

    • Generate a new token
    • Ensure the token hasn't expired
    • Verify the token format is correct
  • "Connection refused":

    • Check if the Pintheon node is running
    • Verify the host and port are correct
    • Check for firewall rules blocking the connection
  • "Request timed out":

    • Increase the timeout value
    • Check network connectivity
    • Verify the node isn't under heavy load
  • "File too large":

    • Check the file size limit on the Pintheon node
    • Consider using chunked uploads for large files
    • Verify available disk space on the node

Next Steps ​

  1. Explore the full API documentation for additional endpoints
  2. Implement file encryption for sensitive data
  3. Set up automatic token refresh
  4. Build a client library for your preferred programming language