{"course":{"slug":"hugging-face","title":"Hugging Face, End to End","url":"https://addaly.com/learn/hugging-face","level":"intermediate","track":"create"},"lesson":{"slug":"tokens-and-gates","title":"A token in a public Space is a token you have given away","url":"https://addaly.com/learn/hugging-face/tokens-and-gates","position":66,"of":75,"minutes":8,"answer":"Scope tokens as narrowly as the job allows, keep them in environment variables and Space secrets rather than in code, and revoke on suspicion rather than reasoning about whether a leak was seen.","keyIdea":"Scope tokens as narrowly as the job allows, keep them in environment variables and Space secrets rather than in code, and revoke on suspicion rather than reasoning about whether a leak was seen."},"body":{"format":"markdown","text":"## Why you need a token at all\n\nMost of the Hub is readable without an account. You will still need a token for four things: private repositories, gated models, uploading anything, and any hosted inference you are billed for. A token is a string beginning `hf_` that stands in for you. Anything holding it *is* you, as far as the Hub is concerned.\n\n## Make one that can do as little as possible\n\nAccess tokens live under your account settings. There are three kinds, and the choice matters more than the two seconds it takes.\n\n- **Fine-grained**: you tick exactly which permissions it has, and can scope it to named repositories or a single organisation. This should be your default.\n- **Read**: can read everything you can read. Convenient, broad.\n- **Write**: can create, modify and delete your repositories. A leaked write token is not an inconvenience.\n\nGive each token a name that says where it lives (`laptop-read`, `render-space`, `ci-upload`) because in six months you will want to revoke one of them without breaking the others, and an untitled list of five tokens gives you no way to choose.\n\n## Where it lives on your machine\n\n```bash\nhf auth login\n```\n\nThis prompts for the token and writes it to a file under your Hugging Face home directory, typically `~/.cache/huggingface/token`. Every library on that machine can read that file. That is the trade for never typing it again, and it is usually the right trade on your own laptop and the wrong one on a shared server.\n\nIn code, never write the token as a literal. Read it from the environment:\n\n```python\nimport os\nfrom huggingface_hub import login\n\nlogin(token=os.environ[\"HF_TOKEN\"])\n```\n\nMost of the libraries pick up an `HF_TOKEN` environment variable on their own, so often you set the variable and write no authentication code at all.\n\n## Gated repositories\n\nSome repositories (Llama, Gemma, a number of speech models and datasets) sit behind a gate. The page shows the licence and a button. Sometimes access is granted instantly. Sometimes there is a short form asking for your name, affiliation and country, and a wait of hours or days for a human. Sometimes you are refused.\n\nThat form is a condition of the licence, not a formality, and the name you give is meant to be your real one.\n\nThe symptoms are worth memorising, because they look alike and mean different things:\n\n- **401**: there is no valid token in the request at all.\n- **403**, or a `GatedRepoError`: the token is valid but that *account* has not been granted access to that repository.\n\nThe second one catches people out constantly. You accepted the terms in a browser while logged in as yourself; the code is running with a token belonging to a service account, a colleague, or a Space. Access is per account, and accepting the terms once does not propagate.\n\n## The rule, stated once\n\n**Anywhere a token is visible to someone else, it is theirs.** A public Space's files are public. A notebook pushed to GitHub is public. A screenshot of your terminal posted in a support thread is public, and the token is legible in it. Automated scanners crawl for the `hf_` pattern within minutes of publication.\n\nHugging Face and GitHub both scan for leaked tokens and will revoke them, which is a safety net rather than a plan: it fires after publication, not before. If you think a token might have been seen, revoke and reissue. That takes thirty seconds and requires no judgement, whereas reasoning about whether anyone actually saw it requires perfect information you do not have.\n\nIn a Space, the token goes in Settings under secrets, with the name `HF_TOKEN`, and never in `app.py`.\n\n## One organisational point\n\nA token is a person, not a service. When someone leaves a team, their tokens keep working until somebody revokes them, and any pipeline built on a departed colleague's token dies the day you do. For anything shared, create an organisation, add a dedicated account or a fine-grained token owned by the organisation, and scope it to the repositories it needs.\n\n## Do this now\n\nOpen your access tokens page. If there is a write token you cannot account for, revoke it. Then create one fine-grained read token, named after the machine you are sitting at, and log in with that."},"check":{"question":"A script that downloads a gated model runs fine on a developer's laptop. Deployed to a server, it fails with a 403 and a gated-repository error. The token is definitely present and definitely valid. What is the most likely cause?","options":[{"text":"The gate rate-limits downloads by IP address, and the server's address has not been approved.","correct":false,"tempting":"Invents a network-level restriction where the control is actually per account."},{"text":"The server's token belongs to a different account, and access to a gated repository is granted per account rather than carried by any valid token.","correct":true},{"text":"The token is a read token where a gated repository requires write scope to accept the terms programmatically.","correct":false,"tempting":"Reads the failure as a missing permission level rather than a missing grant on that account."},{"text":"The acceptance expired, and gated licences must be re-accepted periodically.","correct":false,"tempting":"Treats a persistent grant as a session that lapses, which would fail on the laptop too."}]},"prev":{"slug":"publishing-an-adapter","title":"Shipping the thing you trained","url":"https://addaly.com/learn/hugging-face/publishing-an-adapter"},"next":{"slug":"licences-in-practice","title":"What the licence tag does and does not settle","url":"https://addaly.com/learn/hugging-face/licences-in-practice"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'A token in a public Space is a token you have given away', in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/tokens-and-gates (Accessed: 31 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"A token in a public Space is a token you have given away","courseTitle":"Hugging Face, End to End","module":{"index":8,"slug":"depending-on-it-and-publishing","title":"Depending on it, and publishing so others can"},"answer":null,"keyIdea":"Scope tokens as narrowly as the job allows, keep them in environment variables and Space secrets rather than in code, and revoke on suspicion rather than reasoning about whether a leak was seen.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"why-you-need-a-token-at-all","runs":[{"text":"Why you need a token at all"}]},{"t":"p","runs":[{"text":"Most of the Hub is readable without an account. You will still need a token for four things: private repositories, gated models, uploading anything, and any hosted inference you are billed for. A token is a string beginning "},{"text":"hf_","code":true},{"text":" that stands in for you. Anything holding it "},{"text":"is","i":true},{"text":" you, as far as the Hub is concerned."}]},{"t":"h2","id":"make-one-that-can-do-as-little-as-possible","runs":[{"text":"Make one that can do as little as possible"}]},{"t":"p","runs":[{"text":"Access tokens live under your account settings. There are three kinds, and the choice matters more than the two seconds it takes."}]},{"t":"ul","items":[[{"text":"Fine-grained","b":true},{"text":": you tick exactly which permissions it has, and can scope it to named repositories or a single organisation. This should be your default."}],[{"text":"Read","b":true},{"text":": can read everything you can read. Convenient, broad."}],[{"text":"Write","b":true},{"text":": can create, modify and delete your repositories. A leaked write token is not an inconvenience."}]]},{"t":"p","runs":[{"text":"Give each token a name that says where it lives ("},{"text":"laptop-read","code":true},{"text":", "},{"text":"render-space","code":true},{"text":", "},{"text":"ci-upload","code":true},{"text":") because in six months you will want to revoke one of them without breaking the others, and an untitled list of five tokens gives you no way to choose."}]},{"t":"h2","id":"where-it-lives-on-your-machine","runs":[{"text":"Where it lives on your machine"}]},{"t":"code","lang":"bash","label":"Bash","text":"hf auth login"},{"t":"p","runs":[{"text":"This prompts for the token and writes it to a file under your Hugging Face home directory, typically "},{"text":"~/.cache/huggingface/token","code":true},{"text":". Every library on that machine can read that file. That is the trade for never typing it again, and it is usually the right trade on your own laptop and the wrong one on a shared server."}]},{"t":"p","runs":[{"text":"In code, never write the token as a literal. Read it from the environment:"}]},{"t":"code","lang":"python","label":"Python","text":"import os\nfrom huggingface_hub import login\n\nlogin(token=os.environ[\"HF_TOKEN\"])"},{"t":"p","runs":[{"text":"Most of the libraries pick up an "},{"text":"HF_TOKEN","code":true},{"text":" environment variable on their own, so often you set the variable and write no authentication code at all."}]},{"t":"h2","id":"gated-repositories","runs":[{"text":"Gated repositories"}]},{"t":"p","runs":[{"text":"Some repositories (Llama, Gemma, a number of speech models and datasets) sit behind a gate. The page shows the licence and a button. Sometimes access is granted instantly. Sometimes there is a short form asking for your name, affiliation and country, and a wait of hours or days for a human. Sometimes you are refused."}]},{"t":"p","runs":[{"text":"That form is a condition of the licence, not a formality, and the name you give is meant to be your real one."}]},{"t":"p","runs":[{"text":"The symptoms are worth memorising, because they look alike and mean different things:"}]},{"t":"ul","items":[[{"text":"401","b":true},{"text":": there is no valid token in the request at all."}],[{"text":"403","b":true},{"text":", or a "},{"text":"GatedRepoError","code":true},{"text":": the token is valid but that "},{"text":"account","i":true},{"text":" has not been granted access to that repository."}]]},{"t":"p","runs":[{"text":"The second one catches people out constantly. You accepted the terms in a browser while logged in as yourself; the code is running with a token belonging to a service account, a colleague, or a Space. Access is per account, and accepting the terms once does not propagate."}]},{"t":"h2","id":"the-rule-stated-once","runs":[{"text":"The rule, stated once"}]},{"t":"p","runs":[{"text":"Anywhere a token is visible to someone else, it is theirs.","b":true},{"text":" A public Space’s files are public. A notebook pushed to GitHub is public. A screenshot of your terminal posted in a support thread is public, and the token is legible in it. Automated scanners crawl for the "},{"text":"hf_","code":true},{"text":" pattern within minutes of publication."}]},{"t":"p","runs":[{"text":"Hugging Face and GitHub both scan for leaked tokens and will revoke them, which is a safety net rather than a plan: it fires after publication, not before. If you think a token might have been seen, revoke and reissue. That takes thirty seconds and requires no judgement, whereas reasoning about whether anyone actually saw it requires perfect information you do not have."}]},{"t":"p","runs":[{"text":"In a Space, the token goes in Settings under secrets, with the name "},{"text":"HF_TOKEN","code":true},{"text":", and never in "},{"text":"app.py","code":true},{"text":"."}]},{"t":"h2","id":"one-organisational-point","runs":[{"text":"One organisational point"}]},{"t":"p","runs":[{"text":"A token is a person, not a service. When someone leaves a team, their tokens keep working until somebody revokes them, and any pipeline built on a departed colleague’s token dies the day you do. For anything shared, create an organisation, add a dedicated account or a fine-grained token owned by the organisation, and scope it to the repositories it needs."}]},{"t":"h2","id":"do-this-now","runs":[{"text":"Do this now"}],"role":"try"},{"t":"p","runs":[{"text":"Open your access tokens page. If there is a write token you cannot account for, revoke it. Then create one fine-grained read token, named after the machine you are sitting at, and log in with that."}]}],"check":{"question":[{"text":"A script that downloads a gated model runs fine on a developer’s laptop. Deployed to a server, it fails with a 403 and a gated-repository error. The token is definitely present and definitely valid. What is the most likely cause?"}],"options":[{"text":[{"text":"The gate rate-limits downloads by IP address, and the server’s address has not been approved."}],"correct":false,"tempting":[{"text":"Invents a network-level restriction where the control is actually per account."}]},{"text":[{"text":"The server’s token belongs to a different account, and access to a gated repository is granted per account rather than carried by any valid token."}],"correct":true,"tempting":null},{"text":[{"text":"The token is a read token where a gated repository requires write scope to accept the terms programmatically."}],"correct":false,"tempting":[{"text":"Reads the failure as a missing permission level rather than a missing grant on that account."}]},{"text":[{"text":"The acceptance expired, and gated licences must be re-accepted periodically."}],"correct":false,"tempting":[{"text":"Treats a persistent grant as a session that lapses, which would fail on the laptop too."}]}],"order":[0,3,2,1],"why":null},"prev":{"slug":"publishing-an-adapter","title":"Shipping the thing you trained","minutes":8},"next":{"slug":"licences-in-practice","title":"What the licence tag does and does not settle","minutes":10}}}