# How to Upload Decensored Models to Hugging Face Using Heretic

> Upload decensored models to Hugging Face securely with Heretic. This guide shows you how to use the interactive CLI to push adapters or merged models without saving credentials.

- Repository: [Philipp Emanuel Weidmann/heretic](https://github.com/p-e-w/heretic)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Heretic provides an interactive CLI workflow in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py) that authenticates via temporary tokens, constructs repository IDs, and pushes either LoRA adapters or fully merged decensored models to the Hugging Face Hub without persisting credentials to disk.**

The `p-e-w/heretic` repository includes a built-in mechanism to upload decensored models to Hugging Face using Heretic's command-line interface. This workflow handles authentication, repository naming, visibility settings, and model card generation automatically, ensuring your decensored weights reach the Hub securely.

## Interactive Upload Workflow in Heretic

The upload sequence is implemented in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py) (lines 777–951) and triggers after you select a trial from the interactive menu. The workflow proceeds through eight distinct stages, from token acquisition to model card attachment.

### Authentication and Token Handling

Heretic prioritizes security by avoiding on-disk credential storage. The process begins at lines 777–782:

- First, the library invokes `huggingface_hub.get_token()` to check for a cached token.
- If none exists, `prompt_password` (defined in [`src/heretic/utils.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/utils.py)) interactively requests your personal access token. This prevents writing sensitive credentials to disk, which is critical when working on shared GPU servers.

### Repository Configuration

Once authenticated, Heretic identifies your Hub identity and constructs the target repository:

- `huggingface_hub.whoami(token)` retrieves your username and profile details (lines 783–889), confirming the account that will own the model.
- The default repository ID follows the pattern `<username>/<model-name>-heretic`, derived from `settings.model` in [`src/heretic/config.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/config.py) (lines 891–896).
- You may override this default via an interactive prompt.

### Visibility and Merge Strategy Selection

Before pushing, you configure visibility and artifact type:

- **Visibility**: The prompt at lines 898–904 lets you select **Public** or **Private**, mapping directly to the `private` boolean passed to `push_to_hub`.
- **Merge Strategy**: `obtain_merge_strategy(settings)` (lines 907–913) determines whether to upload a LoRA adapter (`adapter`) or a fully merged model (`merged`). This aligns with the strategy used for local saves.

### Pushing to the Hub

The actual upload occurs at lines 910–924, branching based on the merge strategy:

**For LoRA adapters:**

```python
model.model.push_to_hub(repo_id, private=private, token=token)

```

**For merged models:**

```python
merged_model = model.get_merged_model()
merged_model.push_to_hub(repo_id, private=private, token=token)
model.tokenizer.push_to_hub(repo_id, private=private, token=token)

```

The merged path explicitly pushes both the model weights and the tokenizer to ensure the repository is complete.

### Model Card Generation

Finally, Heretic attaches or creates a model card (lines 927–951):

- If `REPOCARD_NAME` (typically [`README.md`](https://github.com/p-e-w/heretic/blob/main/README.md)) exists in the local model directory, it loads via `ModelCard.load`.
- Otherwise, it attempts to fetch the card from the original model on the Hub.
- If no metadata exists, it initializes a default `ModelCardData` instance to ensure the Hub receives structured metadata.

The card is then pushed to the repository, completing the upload workflow.

## Programmatic Upload Script

While the interactive CLI is the primary interface, you can replicate the upload logic programmatically. The following Python script mirrors the workflow in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py), allowing you to upload decensored models without manual prompting:

```python
from pathlib import Path
import huggingface_hub
from huggingface_hub import ModelCard, ModelCardData

# 1. Get a token (fallback to manual input)

token = huggingface_hub.get_token()
if not token:
    import getpass
    token = getpass.getpass("Hugging Face access token: ")

# 2. Identify the user

user = huggingface_hub.whoami(token)
repo_id = f"{user['name']}/my-model-heretic"  # customize as needed

private = False  # or True for a private repo

# 3. Load your decensored model

# Replace with your actual Heretic model instance

# model = HereticModel.from_pretrained(...)

# 4. Choose merge strategy

strategy = "merged"  # or "adapter"

if strategy == "adapter":
    model.model.push_to_hub(repo_id, private=private, token=token)
else:
    merged = model.get_merged_model()
    merged.push_to_hub(repo_id, private=private, token=token)
    model.tokenizer.push_to_hub(repo_id, private=private, token=token)

# 5. Handle model card

model_path = Path("path/to/local/model")
card_path = model_path / huggingface_hub.constants.REPOCARD_NAME

if card_path.is_file():
    card = ModelCard.load(card_path)
else:
    try:
        card = ModelCard.load(repo_id)
    except Exception:
        card = ModelCard()

if card.data is None:
    card.data = ModelCardData()

card.push_to_hub(repo_id, token=token, private=private)
print(f"✅ Uploaded to https://huggingface.co/{repo_id}")

```

When run after you have already performed a Heretic trial (i.e., you have a `model` object with the decensored weights), this script reproduces the same behaviour as the CLI's "Upload the model to Hugging Face" option.

## Key Source Files and Implementation Details

The upload functionality is distributed across four core modules in the `p-e-w/heretic` repository:

| File | Role | Key Components |
|------|------|----------------|
| [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py) | Interactive CLI and upload orchestration | `prompt_password`, `huggingface_hub.whoami`, `obtain_merge_strategy`, `push_to_hub` calls (lines 777–951) |
| [`src/heretic/config.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/config.py) | Configuration and settings | `Settings` model storing original model path (`settings.model`) used to derive default repository names |
| [`src/heretic/model.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/model.py) | Model abstraction and merging | `get_merged_model()`, `push_to_hub()`, tokenizer handling |
| [`src/heretic/utils.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/utils.py) | User interaction utilities | `prompt_text`, `prompt_password`, `prompt_select` for interactive prompts |

These modules collectively implement the secure, credential-free upload pipeline that distinguishes Heretic's approach to sharing decensored models.

## Summary

- **Heretic's upload workflow** is implemented in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py) (lines 777–951) and provides an interactive, menu-driven interface for pushing decensored models to Hugging Face.
- **Security-first authentication** uses `huggingface_hub.get_token()` with a fallback to `prompt_password`, ensuring tokens remain in memory only and are never written to disk—critical for shared GPU environments.
- **Flexible artifact selection** via `obtain_merge_strategy` lets you upload either a **LoRA adapter** or a **fully merged model**, with the latter including the tokenizer.
- **Automatic model card handling** checks for local [`README.md`](https://github.com/p-e-w/heretic/blob/main/README.md) files, falls back to remote cards, and ensures valid metadata via `ModelCardData` before pushing.

## Frequently Asked Questions

### How does Heretic handle Hugging Face authentication without storing tokens on disk?

Heretic prioritizes security by first attempting to retrieve a cached token via `huggingface_hub.get_token()`. If no token exists, it invokes `prompt_password` (defined in [`src/heretic/utils.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/utils.py)) to request your personal access token interactively. This approach ensures the token remains in memory only and is never written to configuration files, which is essential when operating on shared GPU servers where filesystem credentials could be exposed.

### Should I upload a LoRA adapter or a merged model to Hugging Face?

The choice depends on your distribution goals. Heretic's `obtain_merge_strategy` function (lines 907–913 in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py)) determines the upload artifact:
- **LoRA adapter**: Uploads only the adapter weights via `model.model.push_to_hub()`. This creates a smaller repository and allows users to merge it with the base model locally.
- **Merged model**: Creates a full merged copy using `model.get_merged_model()`, then pushes both the merged weights and the tokenizer. This provides an immediately usable model but results in a larger repository.

### Can I make my uploaded decensored model private on Hugging Face?

Yes. During the interactive upload workflow (lines 898–904 in [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py)), Heretic prompts you to select repository visibility. Your choice maps directly to the `private` boolean parameter passed to `push_to_hub()`. Selecting **Private** creates a repository visible only to you and explicitly invited collaborators, while **Public** makes the model accessible to the entire Hugging Face community.

### What happens if my local model doesn't have a README.md model card?

Heretic handles missing model cards gracefully. In [`src/heretic/main.py`](https://github.com/p-e-w/heretic/blob/main/src/heretic/main.py) (lines 927–951), the code first checks for a local `REPOCARD_NAME` (typically [`README.md`](https://github.com/p-e-w/heretic/blob/main/README.md)). If not found, it attempts to load an existing card from the remote Hub using the original model ID. If neither exists, it initializes a fresh `ModelCard` with a default `ModelCardData` instance to ensure the uploaded repository contains valid metadata. This guarantees that your decensored model appears correctly on the Hugging Face Hub regardless of the original card's presence.