Skip to main content
Teams are groups of users that compete together in CTF games. Each team can participate in multiple games, with separate participation records and scoring for each game.

Team Structure

A team in GZCTF consists of:

Captain

The team leader who created the team and manages members

Members

Users who have accepted invitations to join the team

Participation

Registration records for each game the team joins

Invite System

Token-based system for recruiting new members

Team Properties

Basic Information

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }     // Max 15 characters
    public string? Bio { get; set; }     // Team description
    public string? AvatarHash { get; set; }
    public bool Locked { get; set; }     // Prevents modifications
}
PropertyDescription
NameTeam name (max 15 characters)
BioTeam biography/description
AvatarHashTeam avatar image identifier
LockedWhen true, team cannot be modified
CaptainIdUser ID of the team captain

Team Invitations

Each team has a unique invite code for recruiting members:
public string InviteToken { get; set; } = Guid.NewGuid().ToString("N");
public string InviteCode => $"{Name}:{Id}:{InviteToken}";
1

Captain generates invite

The team captain can regenerate the invite token at any time:
team.UpdateInviteToken(); // Generates new GUID
2

Share invite code

Captain shares the invite code (format: TeamName:123:abc123def456)
3

Member joins

User submits the invite code to join the team
4

Verification

System validates the code matches team name, ID, and current token
Regenerating the invite token invalidates all previously shared invite codes.

Participation

Participation represents a team’s registration in a specific game.

Participation Status

Each participation has one of the following statuses:
Team registered and awaiting approvalWhen AcceptWithoutReview = false, teams start in Pending status and require administrator approval.
public enum ParticipationStatus : byte
{
    Pending = 0,
    Accepted = 1,
    Rejected = 2,
    Suspended = 3,
    Unsubmitted = 4
}

Participation Properties

public class Participation
{
    public int Id { get; set; }
    public ParticipationStatus Status { get; set; }
    public string Token { get; set; }  // Unique participation token
    
    // Relationships
    public int GameId { get; set; }
    public Game Game { get; set; }
    public int TeamId { get; set; }
    public Team Team { get; set; }
    public int? DivisionId { get; set; }
    public Division? Division { get; set; }
    
    // Competition data
    public HashSet<UserParticipation> Members { get; set; }
    public HashSet<GameChallenge> Challenges { get; set; }
    public List<GameInstance> Instances { get; set; }
    public List<Submission> Submissions { get; set; }
    public List<FirstSolve> FirstSolves { get; set; }  // Blood bonuses earned
    public LocalFile? Writeup { get; set; }
}
The participation token is a cryptographically signed unique identifier used for:
  • Dynamic flag generation
  • Team-specific container instances
  • Preventing flag sharing

Divisions

Divisions allow games to segment participants into separate tracks with different permissions and scoreboards.

Division Structure

public class Division
{
    public int Id { get; set; }
    public int GameId { get; set; }
    public string Name { get; set; }
    public string? InviteCode { get; set; }
    public GamePermission DefaultPermissions { get; set; }
}

Common Use Cases

Separate scoreboards for different skill levels:
  • Student Division: RankOverall permission enabled
  • Professional Division: RankOverall disabled (unofficial)
Separate divisions by geography:
  • Division-specific invite codes
  • Regional scoreboards
  • Same challenges, independent rankings
Control challenge visibility per division:
  • Beginner: Access to easy challenges only
  • Advanced: Full challenge access
  • Configured via DivisionChallengeConfig

Division Permissions

Divisions use the GamePermission flag system to control access:
Permissions that apply to the entire division:
  • JoinGame: Can register for the game through this division
  • RankOverall: Appears on the overall scoreboard
  • RequireReview: Requires admin approval (overrides game setting)
public class DivisionChallengeConfig
{
    public int DivisionId { get; set; }
    public int ChallengeId { get; set; }
    public GamePermission Permissions { get; set; } = GamePermission.All;
}
Use GamePermission.All (int.MaxValue) to grant all current and future permissions by default.

Example: Multi-Division Setup

var officialDivision = new Division
{
    Name = "Official",
    InviteCode = "OFFICIAL2024",
    DefaultPermissions = GamePermission.All  // Full access
};

var observerDivision = new Division
{
    Name = "Observer",
    InviteCode = "OBSERVER2024",
    DefaultPermissions = 
        GamePermission.JoinGame |
        GamePermission.ViewChallenge |
        GamePermission.SubmitFlags
        // No GetScore or RankOverall - can play but not compete
};

Team Constraints

Games can enforce limits on team composition:

Team Size Limit

public int TeamMemberCountLimit { get; set; }  // 0 = unlimited
  • Set in game configuration
  • Enforced when members join teams
  • Captain is included in the count
Teams exceeding the limit cannot register for the game.

Container Limit

public int ContainerCountLimit { get; set; } = 3;
Limits simultaneous container instances per team:
  • Prevents resource exhaustion
  • Teams must stop containers to start new ones
  • Default: 3 concurrent containers

Model References

Location: ~/workspace/source/src/GZCTF/Models/Data/Team.cs
public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string? Bio { get; set; }
    public string? AvatarHash { get; set; }
    public bool Locked { get; set; }
    public string InviteToken { get; set; }
    
    // Relationships
    public Guid CaptainId { get; set; }
    public UserInfo? Captain { get; set; }
    public HashSet<UserInfo> Members { get; set; }
    public List<Participation> Participations { get; set; }
    public HashSet<Game>? Games { get; set; }
}

Games

Learn about game configuration and lifecycle

Scoring

Understand how teams earn points

Challenges

Explore challenge types and structure

Admin Guide

Manage user permissions and roles

Build docs developers (and LLMs) love