# IP Flag (ipflag)
IP Flag is a tiny, resolver-pluggable Rust crate that turns IP addresses into a human-friendly country display (flag + country code). It is designed for communities, forums, comment sections, moderation logs, analytics dashboards, and anywhere an IP string is hard to read at a glance.
The core idea is simple:
- IP Flag does display.
- IP Flag does not ship geolocation data.
- IP Flag does not call external APIs.
- IP Flag does not require updates.
If you want real “IP → country” resolution, you provide it via a resolver implementation (or via a separate extension crate). This keeps the core maintenance-free and makes the ecosystem extensible.
---
## Why IP Flag exists
Raw IP addresses are noisy and not human-friendly.
In real community moderation, the question is often not “what is the exact IP?”, but “what pattern do we see across many comments?” A country flag or country code makes it easy to scan a thread and quickly notice clusters and anomalies.
IP Flag does not make claims about intent or manipulation. It only helps humans see patterns more clearly.
---
## What IP Flag does (and does not)
### IP Flag does
- Parse IPv4/IPv6
- Classify IP scope:
- `Private` (LAN / internal)
- `Special` (loopback, multicast, documentation ranges, etc.)
- `Public` (eligible for resolution)
- if `Public`, call your resolver and display:
- Flag emoji (🇰🇷, 🇺🇸, 🇷🇺, ...)
- Country code (KR, US, RU, ...)
- Provide formatting helpers for common UI output styles
### IP Flag does not
- Bundle MaxMind or any GeoIP database
- Contact third-party APIs
- Track, store, or log anything
- Decide what is “suspicious”
- Maintain an ever-changing list of countries or names
---
## Installation
Add the crate to your `Cargo.toml`:
```toml
ipflag = "0.1.0"
```
Then import what you need:
- `tag_ip` / `tag_addr` to generate tags
- `IpResolver` to plug in your own resolution logic
- `NoopResolver` for “display-only” usage without resolution
---
## Core usage
### Use IP Flag by itself (no resolver data)
This mode is still useful because it cleanly categorizes internal/special addresses and gives you a consistent display output.
Example:
```rust
use ipflag::{tag_ip, NoopResolver};
fn main() {
let t1 = tag_ip(&NoopResolver, "192.168.0.10").unwrap();
println!("{}", t1); // 🏠 PRIVATE
let t2 = tag_ip(&NoopResolver, "127.0.0.1").unwrap();
println!("{}", t2); // ⚙️ SPECIAL
let t3 = tag_ip(&NoopResolver, "8.8.8.8").unwrap();
println!("{}", t3); // 🌐 UNKNOWN
}
```
What you get here is:
- A reliable “this is internal” / “this is special” / “this is public but unknown” signal
- A stable output format suitable for UI and logs
- Zero dependencies and zero update requirements
### 2) Provide a resolver (real country flags)
To show real countries, you implement one trait: `IpResolver`.
This resolver can be anything:
- a MaxMind reader
- an IP-to-country map from your own data source
- a corporate CDN header mapping
- a custom enterprise threat intel source
- a quick prototype rule-based resolver
Example resolver (hard-coded for demonstration):
```rust
use ipflag::{CountryCode, IpResolver, tag_ip};
use std::net::IpAddr;
struct DemoResolver;
impl IpResolver for DemoResolver {
type Error = core::convert::Infallible;
fn resolve(&self, _ip: IpAddr) -> Result<Option<CountryCode>, Self::Error> {
Ok(CountryCode::new("KR"))
}
}
fn main() {
let tag = tag_ip(&DemoResolver, "8.8.8.8").unwrap();
println!("{}", tag); // 🇰🇷 KR
}
```
---
## How resolution works
IP Flag calls your resolver only for Public IPs.
- Private IPs never call the resolver → always `🏠 PRIVATE`
- Special IPs never call the resolver → always `⚙️ SPECIAL`
- Public IPs call the resolver:
- resolver returns `Some(CountryCode)` → `IpTag::Country(code)` → displays `🇰🇷 KR`
- resolver returns None → `IpTag::Unknown` → displays `🌐 UNKNOWN`
This is important for correctness, performance, and privacy. It prevents “fake resolution” for private and special ranges and keeps your resolver focused on real public IPs only.
---
## Output formats (UI-friendly)
IpTag `implements` Display, so you can directly print it:
```Rust
println!("{}", tag); // default output
```
If you want different display styles, use `TagFormat`:
```rust
use ipflag::{TagFormat};
println!("{}", tag.format(TagFormat::FlagAndCode)); // "🇰🇷 KR"
println!("{}", tag.format(TagFormat::FlagOnly)); // "🇰🇷"
println!("{}", tag.format(TagFormat::CodeOnly)); // "KR"
```
For non-country tags, the default output is:
- `🏠 PRIVATE`
- `⚙️ SPECIAL`
- `🌐 UNKNOWN`
This is intentional: it keeps log output consistent and predictable.
---
## Minimal “community comment display” example
A typical usage is to append the tag next to a comment’s “anonymous” label:
```rust
use ipflag::{tag_ip, NoopResolver, TagFormat};
fn display_anonymous(ip: &str) -> String {
let tag = tag_ip(&NoopResolver, ip).unwrap();
format!("Anonymous {}", tag.format(TagFormat::FlagOnly))
}
fn main() {
println!("{}", display_anonymous("192.168.1.10")); // Anonymous 🏠
println!("{}", display_anonymous("8.8.8.8")); // Anonymous 🌐
}
```
In production, replace `NoopResolver` with your real resolver implementation.
---
## Privacy and security notes
- IP Flag does not perform network requests.
- IP Flag does not store IPs.
- IP Flag does not log anything.
- Your resolver may, depending on your implementation.
In community settings, you should consider privacy laws and policies. A common approach is masking IPs in UI and logs while still using flags/codes for pattern visibility.
---
## Non-goals
- “Detect manipulation” or “label someone as suspicious”
- Provide absolute truth about user identity or nationality
- Replace moderation judgment
- Provide a global geolocation database
IP Flag is a display utility. The interpretation is yours.
---
## Contributing
Contributions that keep the core minimal and stable are welcome:
- Bug fixes
- Better IPv4/IPv6 scope classification edge cases
- Output formatting improvements
- More examples and documentation
Anything that adds bundled data sources or external network calls should live in extension crates, not in the core.
---
## License
[Apache-2.0](LICENSE)